fix(import): rewrite download_file helper to output curl response directly to file

This prevents memory exhaustion when downloading large files
This commit is contained in:
Yassine Doghri 2024-06-05 18:46:23 +00:00
parent 281eefc6a3
commit eb7ad2f7e1
3 changed files with 25 additions and 40 deletions

View File

@ -136,7 +136,9 @@ if (! function_exists('publication_pill')) {
$customClass .
'">' .
$label .
($publicationStatus === 'with_podcast' ? '<Icon glyph="error-warning" class="flex-shrink-0 ml-1 text-lg" />' : '') .
($publicationStatus === 'with_podcast' ? icon('error-warning-fill', [
'class' => 'flex-shrink-0 ml-1 text-lg',
]) : '') .
'</span>';
}
}

View File

@ -9,47 +9,12 @@ declare(strict_types=1);
*/
use CodeIgniter\Files\File;
use CodeIgniter\HTTP\ResponseInterface;
use Config\Mimes;
use Config\Services;
if (! function_exists('download_file')) {
function download_file(string $fileUrl, string $mimetype = ''): File
{
$client = Services::curlrequest();
$response = $client->get($fileUrl, [
'headers' => [
'User-Agent' => 'Castopod/' . CP_VERSION,
],
]);
// redirect to new file location
$newFileUrl = $fileUrl;
while (
in_array(
$response->getStatusCode(),
[
ResponseInterface::HTTP_MOVED_PERMANENTLY,
ResponseInterface::HTTP_FOUND,
ResponseInterface::HTTP_SEE_OTHER,
ResponseInterface::HTTP_NOT_MODIFIED,
ResponseInterface::HTTP_TEMPORARY_REDIRECT,
ResponseInterface::HTTP_PERMANENT_REDIRECT,
],
true,
)
) {
$newFileUrl = trim($response->header('location')->getValue());
$response = $client->get($newFileUrl, [
'headers' => [
'User-Agent' => 'Castopod/' . CP_VERSION,
],
'http_errors' => false,
]);
}
$fileExtension = pathinfo(parse_url($newFileUrl, PHP_URL_PATH), PATHINFO_EXTENSION);
$fileExtension = pathinfo(parse_url($fileUrl, PHP_URL_PATH), PATHINFO_EXTENSION);
$extension = $fileExtension === '' ? Mimes::guessExtensionFromType($mimetype) : $fileExtension;
$tmpFilename =
time() .
@ -57,9 +22,26 @@ if (! function_exists('download_file')) {
bin2hex(random_bytes(10)) .
'.' .
$extension;
$tmpfileKey = WRITEPATH . 'uploads/' . $tmpFilename;
file_put_contents($tmpfileKey, $response->getBody());
$tmpfilePath = WRITEPATH . 'uploads/' . $tmpFilename;
return new File($tmpfileKey);
$file = fopen($tmpfilePath, 'w');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $fileUrl);
// output directly to file
curl_setopt($ch, CURLOPT_FILE, $file);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['User-Agent: Castopod/' . CP_VERSION]);
// follow redirects up to 20, like Apple Podcasts
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
curl_exec($ch);
curl_close($ch);
fclose($file);
return new File($tmpfilePath);
}
}

View File

@ -39,6 +39,7 @@ $isEpisodeArea = isset($podcast) && isset($episode);
<div class="flex flex-wrap items-center truncate">
<?php if (($isEpisodeArea && $episode->is_premium) || ($isPodcastArea && $podcast->is_premium)): ?>
<div class="inline-flex items-center">
<?php // @icon('exchange-dollar-fill')?>
<IconButton uri="<?= route_to('subscription-list', $podcast->id) ?>" glyph="exchange-dollar-fill" variant="secondary" size="large" class="p-0 mr-2 border-0"><?= ($isEpisodeArea && $episode->is_premium) ? lang('PremiumPodcasts.episode_is_premium') : lang('PremiumPodcasts.podcast_is_premium') ?></IconButton>
<Heading tagName="h1" size="large" class="truncate"><?= $this->renderSection('pageTitle') ?></Heading>
</div>