Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stream an large file from S3 to a laravel view

I have this mostly working but having a tough time finalizing it.

For now I have a simple route:

Route::get('file/{id}/', 'FileController@fileStream')->name('file');

this route connects to an action in the FileController:

public function fileStream($id){

    $audio = \App\Audio::where('id', $id)->first();

    $client = S3Client::factory([
        'credentials' => [
            'key'    => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
        ],
        'region' => env('S3REGION'),
        'version' => 'latest',
    ]);


    // Register the stream wrapper from an S3Client object
    $client->registerStreamWrapper();

    if ($stream = fopen('s3://[bucket_name]/'. $audio->audio_url, 'r')) {
        while (!feof($stream)) {
            echo fread($stream, 1024);
        }
        fclose($stream);
    }    
}

This works to the browser: if I go to a url: /file/1 it looks up the right file, and in a clean browser window I get:

enter image description here

And then in my view I am trying to output the audio like:

   <audio>
      <source src="{{ url('file', ['id' => $section->id]) }}" type="{{ $section->audio_mime_type}}"></audio>
   </audio>

But no player is getting output to the screen.

TIA

like image 812
TJ Sherrill Avatar asked Jul 31 '18 21:07

TJ Sherrill


3 Answers

You should use Laravel Streamed response

return response()->streamDownload(function () use ($audio) {
    if ($stream = fopen('s3://[bucket_name]/'. $audio->audio_url, 'r')) {
        while (!feof($stream)) {
            echo fread($stream, 1024);
            flush();
        }
        fclose($stream);
    }    
}, 'file-name.ext');
like image 97
Hieu Le Avatar answered Oct 10 '22 00:10

Hieu Le


//Get Url from s3 using 
$fileUrl = \Storage::disk('s3')->url($filePath);
$fileName = 'name_of_file.extension';
//Set headers
header('Content-Description: File Transfer');
header('Content-Disposition: attachment; filename='.$fileName);

if (!($stream = fopen($response, 'r'))) {
  throw new \Exception('Could not open stream for reading file: 
   ['.$fileName.']');
}
// Check if the stream has more data to read
while (!feof($stream)) {
  // Read 1024 bytes from the stream
  echo fread($stream, 1024);
}
// Be sure to close the stream resource when you're done with it
fclose($stream);
like image 29
Subhash Diwakar Avatar answered Oct 10 '22 01:10

Subhash Diwakar


Use Laravel/Symfony Response class. Echoing the response could not be setting the right headers.

Even if the headers are set up correctly, you are relying on the echo in the controller action, therefore you should do exit(0); at the end of the controller. Bear in mind that this is rather ugly and it kills the script, you should always aim to use Response classes mentioned above.

like image 1
Jan Richter Avatar answered Oct 10 '22 00:10

Jan Richter