Zpracování stahování souborů z adres URL v systému Laravel
V tomto článku se dozvíte, jak v aplikaci Laravel stahovat soubory z externích adres URL. Budete to dělat jak pro uložení souboru na serveru, tak pro jeho odeslání uživateli ke stažení. Vše se odehrává uvnitř běžného kódu Laravelu, například v kontrolérech nebo úlohách. Zaměříme se pouze na praktické, kopírovatelné vzory, které můžete přizpůsobit svému vlastnímu projektu.
Základní nastavení ovladače
Všechny níže uvedené příklady předpokládají volání metody řadiče z trasy. Například:
// routes/web.php use App\Http\Controllers\FileDownloadController;use Illuminate\Support\Facades\Route; Route::get('/download-remote', [FileDownloadController::class, 'downloadRemote']);Route::get('/cache-remote', [FileDownloadController::class, 'cacheRemote']);
// app/Http/Controllers/FileDownloadController.php namespace App\Http\Controllers; use Illuminate\Http\Request;use Illuminate\Support\Facades\Http;use Illuminate\Support\Facades\Storage; class FileDownloadController extends Controller{ // methods will go here}
Nemusíte svůj kód strukturovat přesně takto, ale díky speciálnímu kontroléru jsou příklady přehledné.
Přímé streamování vzdálené adresy URL do prohlížeče
Někdy chcete pouze převzít vzdálenou adresu URL souboru a nechat uživatele stáhnout jej prostřednictvím aplikace Laravel, aniž byste jej museli ukládat na disk. To je běžné, když stahujete přes proxy server svou aplikaci kvůli ověřování nebo protokolování.
Můžete to udělat pomocí response()->streamDownload() a obyčejný proud PHP:
// app/Http/Controllers/FileDownloadController.php public function downloadRemote(Request $request){ $url = $request->query('url'); // example: https://example.com/file.pdf if (! $url) { abort(400, 'Missing url parameter'); } $fileName = basename(parse_url($url, PHP_URL_PATH)) ?: 'downloaded-file'; return response()->streamDownload(function () use ($url) { $read = fopen($url, 'r'); if (! $read) { abort(502, 'Unable to open remote file'); } while (! feof($read)) { echo fread($read, 1024 * 1024); // 1 MB chunks } fclose($read); }, $fileName);}
Co to dělá:
- Čte
urlparametr dotazu z požadavku. - Používá
streamDownloadstreamovat vzdálený soubor přímo do prohlížeče. - Používá
fopenafreadve smyčce, takže soubor není zcela načten do paměti. - vybere název souboru z cesty URL, nebo se vrátí zpět na adresu
downloaded-file.
Okrajové případy, na které je třeba dávat pozor:
allow_url_fopenmusí být v PHP povoleno, abyfopen($url, 'r')do práce.- Pokud je vzdálený server pomalý nebo nedostupný, bude váš požadavek také pomalý nebo selže.
- Před povolením stahování můžete přidat vlastní logiku autorizace.
Stažení souboru z adresy URL a jeho uložení na disk
Velmi často chcete jednou načíst vzdálený soubor, uložit jej někam na úložný disk a pak jej odtud obsluhovat. To se hodí pro ukládání vzdálených aktiv, faktur, zpráv a podobných souborů do mezipaměti.
Jednoduchým přístupem je použití klienta Laravel HTTP a fasády Úložiště.
// app/Http/Controllers/FileDownloadController.php public function cacheRemote(Request $request){ $url = $request->query('url'); if (! $url) { abort(400, 'Missing url parameter'); } $response = Http::timeout(30)->get($url); if (! $response->successful()) { abort(502, 'Failed to download remote file'); } $extension = pathinfo(parse_url($url, PHP_URL_PATH) ?? '', PATHINFO_EXTENSION); $fileName = 'remote_' . time() . ($extension ? ".{$extension}" : ''); $path = "downloads/{$fileName}"; Storage::disk('local')->put($path, $response->body()); return response()->json([ 'stored_as' => $path, 'disk' => 'local', ]);}
Co to dělá:
- Použije klienta HTTP k získání vzdálené adresy URL s časovým limitem.
- Kontroly
successful()abyste neukládali chybové stránky jako soubory. - Odvozuje jednoduché rozšíření z cesty URL.
- zapíše nezpracované tělo odpovědi do
storage/app/downloads/...pomocílocaldisk. - Vrátí malé užitečné zatížení JSON s místem uložení souboru.
Pro typické malé a střední soubory je to v pořádku. U velmi velkých souborů tento přístup načte celý soubor do paměti jednou, takže pokud na tom záleží, použijte v další části přístup založený na streamování.
Streamování adresy URL přímo do úložiště pomocí sink HTTP
U velkých souborů obvykle nechcete mít v paměti celý soubor. HTTP klient Laravel je postaven na platformě Guzzle, která podporuje funkci sink který přenáší odpověď přímo do souboru.
// app/Http/Controllers/FileDownloadController.php use GuzzleHttp\Psr7\Utils; public function cacheRemoteStreamed(Request $request){ $url = $request->query('url'); if (! $url) { abort(400, 'Missing url parameter'); } $extension = pathinfo(parse_url($url, PHP_URL_PATH) ?? '', PATHINFO_EXTENSION); $fileName = 'remote_' . time() . ($extension ? ".{$extension}" : ''); $relativePath = "downloads/{$fileName}"; $absolutePath = Storage::disk('local')->path($relativePath); $resource = Utils::tryFopen($absolutePath, 'w'); Http::withOptions([ 'sink' => $resource, 'timeout' => 60, ])->get($url); return response()->json([ 'stored_as' => $relativePath, 'disk' => 'local', ]);}
Co to dělá:
- Vytvoří zapisovatelný handle souboru uvnitř
localkořenový adresář disku (ve výchozím nastavenístorage/app/private/downloads/...). - předá tento handle klientovi HTTP prostřednictvím příkazu
sinkmožnost. - Guzzle přenáší odpověď do souboru tak, jak přichází.
- Využití paměti zůstává nízké i u velkých souborů.
Pokud chcete, můžete místo prostředku předat celý řetězec cesty k souboru do pole sinka Guzzle vám soubor otevře.
Umožnění uživateli stáhnout uložený soubor
Jakmile je soubor uložen na disku, obvykle chcete uživateli umožnit jeho stažení. Pokud jste soubor uložili pomocí Storage na interním disku (například local), nejjednodušší způsob je Storage::download().
// app/Http/Controllers/FileDownloadController.php public function downloadCached(Request $request){ $path = $request->query('path'); // for example downloads/remote_1700000000.pdf if (! $path) { abort(400, 'Missing path parameter'); } if (! Storage::disk('local')->exists($path)) { abort(404, 'File not found'); } return Storage::disk('local')->download($path);}
Co to dělá:
- Čte
pathkterý byl vrácen při ukládání souboru do mezipaměti. - Zkontroluje, zda soubor existuje na
localdisk. - Vrací odpověď na stahování, která donutí prohlížeč stáhnout soubor.
Můžete také přepsat název stahovaného souboru a hlavičky:
return Storage::disk('local')->download($path, 'report.pdf', [ 'Content-Type' => 'application/pdf',]);
Kombinace vzdáleného stahování a stahování uživatelem v jednom požadavku
Někdy chcete načíst soubor z adresy URL a okamžitě jej odeslat do prohlížeče, aniž byste si uchovávali kopii. Můžete kombinovat klienta HTTP a streamDownload k tomu.
// app/Http/Controllers/FileDownloadController.php public function proxyRemote(Request $request){ $url = $request->query('url'); if (! $url) { abort(400, 'Missing url parameter'); } $fileName = basename(parse_url($url, PHP_URL_PATH)) ?: 'downloaded-file'; $response = Http::timeout(60)->get($url); if (! $response->successful()) { abort(502, 'Failed to download remote file'); } $contentType = $response->header('Content-Type', 'application/octet-stream'); return response()->streamDownload(function () use ($response) { echo $response->body(); }, $fileName, [ 'Content-Type' => $contentType, ]);}
Co to dělá:
- Načte vzdálenou adresu URL pomocí klienta HTTP.
- Ověřuje, že odpověď byla úspěšná.
- Používá dálkový ovladač
Content-Typehlavičku při odesílání souboru do prohlížeče. - Zabalí tělo do
streamDownloadaby se do prohlížeče stáhla správná příloha.
Tento přístup je vhodný pro malé nebo středně velké soubory. U velmi velkých souborů byste se měli držet varianty streamování, která neukládá celé tělo odpovědi do paměti.
Používání front nebo úloh cronu ke stahování z adres URL
Pokud stahování vzdáleného souboru trvá dlouho, obvykle nechcete blokovat běžný webový požadavek. Běžný vzor je:
- Požadavek HTTP odešle úlohu, která stáhne a uloží vzdálený soubor pomocí jedné z výše uvedených metod mezipaměti.
- Úloha zapíše cestu do databáze.
- Samostatná akce řadiče později zpřístupní soubor prostřednictvím
Storage::download().
Přesné nastavení fronty a plánování závisí na vaší aplikaci, ale můžete znovu použít stejný systém. cacheRemote or cacheRemoteStreamed kód uvnitř třídy úloh.
Závěr
Viděli jste, jak stahovat soubory z externích adres URL uvnitř aplikace Laravel.
Vzdálené soubory můžete streamovat přímo do prohlížeče, ukládat je do mezipaměti nebo obojí.
Víte také, jak pracovat s malými soubory pomocí jednoduchého Http::get volání a velké soubory pomocí možnosti HTTP sink.
Pomocí těchto vzorů můžete pokrýt většinu reálných požadavků na stahování z adresy URL bez dalších balíčků.