6

I know there have already been questions about downloading files with Laravel, but all I found deal with local files.

So, normally, I would do something like this:

$path = storage_path().'/my-file.pdf';
$name = 'new-name.pdf';
$headers = '....';

return response()->download($path, $name, $headers);

But what do I do, when the file I need to download is external, laying under www.my-storage.net/files/123/my-file.pdf ?

I could use the copy() function, or a combination of file_get_contents() and Laravel's Storage::put(), but I don't want to store the file locally (and temporarily) every time a download is being processed. Instead, I want to download the external file directly via user's browser.

In plain PHP, it would be something like this. But...

(1) How do I prepare such Laravel download response?

(2) How do I make sure the Laravel solution stays efficient (so, e.g. no memory leaks)?

lesssugar
  • 15,486
  • 18
  • 65
  • 115

1 Answers1

10

Something like the following would work:

 return response()->stream(function () {
      //Can add logic to chunk the file here if you want but the point is to stream data
      readfile("remote file");
 },200, [ "Content-Type" => "application/pdf", 
          "Content-Length" => "optionally add this if you know it", 
           "Content-Disposition" => "attachment; filename=\"filename.pdf\"" 
]);

This works by using Symphony's StreamedResponse

apokryfos
  • 38,771
  • 9
  • 70
  • 114
  • Some of the files are of the size of 100MB. Any known concerns about using the `stream()` method with larger files? Should I maybe chunk the file anyway? – lesssugar May 23 '17 at 10:00
  • @lesssugar This particular solution uses `readfile` which does not pre-load the file but rather streams it, however take a look at the note at [readfile()](http://php.net/manual/en/function.readfile.php) where it says: *readfile() will not present any memory issues, even when sending large files, on its own. If you encounter an out of memory error ensure that output buffering is off with ob_get_level().* – apokryfos May 23 '17 at 10:02
  • Are you sure the callback is correct? Currently, the browser outputs file contents instead of downloading the file. I'm passing the file URL in place of the `'remote file'` value. I also tried setting `"Content-Type: application/octet-stream"` – lesssugar May 23 '17 at 10:12
  • My bad, I had another output before the response got returned, which broke the call. Thanks. – lesssugar May 23 '17 at 10:55