Luke
Posted on December 13, 2019
Background
In my role as a developer at Reviews.io we often have customers who want to export large datasets from our Dashboard. Large dynamic file downloads can be problematic as they introduce the scope for memory exhaustion.
One way in which we tackle this issue is by streaming our file downloads using Laravel's Response Streaming.
Streamed Downloads
Sometimes you may wish to turn the string response of a given operation into a downloadable response without having to write the contents of the operation to disk. You may use the streamDownload method in this scenario. This method accepts a callback, file name, and an optional array of headers as its arguments
The code
Let's start by declaring a route to get our download file from, I've opted to make the file retrievable by making a get request to /download
with an optional number of rows we want the file to contain.
<?php
Route::get('download/{rows?}', ['uses' => 'StreamedDownloadController@download']);
Now we need to implement our Controller Method download
N.B. I'm using Faker here to generate the data, in reality this would be fetched from a database but the idea is essentially the same.
First of we need to define the method on our controller.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Faker;
class StreamedDownloadController extends Controller
{
public function download($rows = 50000)
{
We've specified a default value for the number of rows we want to write out to the file.
// callback function that writes to php://output
$callback = function() use ($rows) {
// Open output stream
$handle = fopen('php://output', 'w');
// Add CSV headers
fputcsv($handle, [
'Name',
'Address',
]);
// Generate a faker instance
$faker = Faker\Factory::create();
// add the given number of rows to the file.
for ($i=0; $i < $rows ; $i++) {
$row = [
$faker->name,
$faker->address,
];
fputcsv($handle, $row);
}
// Close the output stream
fclose($handle);
};
This is the callback method that will process writing the file out to the php:output
buffer.
Next up we specify the Content-Type so that the browser knows what format the file is supposed to be in.
// build response headers so file downloads.
$headers = [
'Content-Type' => 'text/csv',
];
And finally we return a streamDownload()
response, that executes the callback, writing the generated file out.
// return the response as a streamed response.
return response()->streamDownload($callback, 'download.csv', $headers);
}
}
Posted on December 13, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.