TJ Sherrill
TJ Sherrill

Reputation: 2645

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

Upvotes: 6

Views: 10717

Answers (3)

Subhash Diwakar
Subhash Diwakar

Reputation: 209

//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);

Upvotes: 4

Hieu Le
Hieu Le

Reputation: 8415

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');

Upvotes: 5

Jan Richter
Jan Richter

Reputation: 2086

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.

Upvotes: 1

Related Questions