|

Stream Files From S3

When using S3 as our external storage, sometimes we have to let users download files from S3. If file is too large, it may not fit in the memory. The solution is to stream file into user’s browser straight from S3. Let’s take a look how to do it with Laravel’s Filesystem.

$path = 'path/to/my/report.csv'
$adapter = Storage::getAdapter();
$client = $adapter->getClient();
$client->registerStreamWrapper();

$object = $client->headObject([
    'Bucket' => $adapter->getBucket(),
    'Key' => $path,
]);

$fileName = basename($path);

$headers = [
    'Last-Modified' => $object['LastModified'],
    'Accept-Ranges' => $object['AcceptRanges'],
    'Content-Length' => $object['ContentLength'],
    'Content-type'        => 'text/csv',
    'Content-Disposition' => 'attachment; filename='. $fileName,
];


return response()->stream(function () use ($adapter, $export) {
    if ($stream = fopen("s3://{$adapter->getBucket()}/". $path, 'r')) {
        while (!feof($stream)) {
            echo fread($stream, 1024);
        }
        fclose($stream);
    }    
}, 200, $headers);

There are 2 main things to pay attention to: 1) we need to register s3 stream wrapper ($client->registerStreamWrapper()) 2) we need to set Content-Disposition header to allow user’s browser to force a download.

The streaming is easy enough to do if you are using plain PHP. Please take a look at the Resources Used links to get an idea how to do that.

Resources Used

https://aws.amazon.com/blogs/developer/amazon-s3-php-stream-wrapper/

https://gist.github.com/fideloper/6ada632650d8677ba23963ab4eae6b48

Share this article

Similar Posts

  • Upload to FTP with PHP

    $fp = fopen(‘https://www.example.com/pdfdoc’, ‘r’); $user = “sammy”; $pass = “password”; $ftp_server = “192.168.10.10”; //should be wrapped in try catch to properly handle errors $ftp_conn = ftp_ssl_connect($ftp_server); $login = ftp_login($ftp_conn, $user, $pass); ftp_chdir($ftp_conn, ‘path/to/folder’); //can also use ftp_pwd ftp_pasv($ftp_conn, true); //passive mode ftp_fput($ftp_conn, “mydocument.pdf”, $fp, FTP_BINARY); fclose($fp); ftp_close($ftp_conn); Above code can be used to upload a…

    Share this article
  • Using Local and Public Disks

    Having vagrant run on Windows host machine may cause some problems with symbolic links. After trying to make symbolic links to work and failing, I decided to use public disk in development and local disk in production. php artisan storage:link makes symbolic link from “public/storage” to “storage/app/public”.  When developing on windows-vagrant this command will not work properly,…

    Share this article
  • Replace Funky Characters While Importing CSV

    Sometimes uploaded text/csv file may have non-utf8 or other funky characters using the function below. public static function processUploadedBundles($request) { $content = file_get_contents($request->file(‘uploadedFile’)->getRealPath()); $lines = explode(PHP_EOL, $content); $array = []; foreach ($lines as $line) { $arrayCsv = str_getcsv($line, “,”); $arrayCsv = array_map(function($value){ return preg_replace(‘/[\x00-\x1F\x7F-\xFF]/’, ”, $value); }, $arrayCsv); $array[] = $arrayCsv; } return $array; }…

    Share this article
  • Complex Eloquent Query

    A query below selects products that need to be updated in a remote application. It takes quantities of products in host application that are connected to source products application. On top of that it looks at all the orders that are in “Pending” status and reserves quantities for those products. SELECT products.quantity_available, connector_products.stock_id, products.id, connector_products.sku,…

    Share this article
  • |

    Adding Virtual Box Bridged Network Adapter to Laravel Homestead

    Vagrant allows you to configure bridged network adapter, so your vagrant(homestead) box can be seen on local network.  I order to make this possible by adding a line of code to homestear.rb script in scripts folder. I opted to add this line after the following code. if settings.has_key?(“networks”) settings[“networks”].each do |network| config.vm.network network[“type”], ip: network[“ip”], bridge: network[“bridge”]…

    Share this article