Download from Laravel storage without loading whole file in memory

后端 未结 6 770
渐次进展
渐次进展 2020-12-13 06:43

I am using Laravel Storage and I want to serve users some (larger than memory limit) files. My code was inspired from a post in SO and it goes like this:

$fs         


        
6条回答
  •  南笙
    南笙 (楼主)
    2020-12-13 07:02

    Instead of loading the whole file into memory at once, try to use fread to read and send it chunk by chunk.

    Here is a very good article: http://zinoui.com/blog/download-large-files-with-php

    getDriver();
    
    $fileName = 'bigfile';
    
    $metaData = $fs->getMetadata($fileName);
    $handle = $fs->readStream($fileName);
    
    header('Pragma: public');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Cache-Control: private', false);
    header('Content-Transfer-Encoding: binary');
    header('Content-Disposition: attachment; filename="' . $metaData['path'] . '";');
    header('Content-Type: ' . $metaData['type']);
    
    /*
        I've commented the following line out.
        Because \League\Flysystem\Filesystem uses int for file size
        For file size larger than PHP_INT_MAX (2147483647) bytes
        It may return 0, which results in:
    
            Content-Length: 0
    
        and it stops the browser from downloading the file.
    
        Try to figure out a way to get the file size represented by a string.
        (e.g. using shell command/3rd party plugin?)
    */
    
    //header('Content-Length: ' . $metaData['size']);
    
    
    $chunkSize = 1024 * 1024;
    
    while (!feof($handle)) {
        $buffer = fread($handle, $chunkSize);
        echo $buffer;
        ob_flush();
        flush();
    }
    
    fclose($handle);
    exit;
    ?>
    

    Update

    A simpler way to do this: just call

    if (ob_get_level()) ob_end_clean();
    

    before returning a response.

    Credit to @Christiaan

    //disable execution time limit when downloading a big file.
    set_time_limit(0);
    
    /** @var \League\Flysystem\Filesystem $fs */
    $fs = Storage::disk('local')->getDriver();
    
    $fileName = 'bigfile';
    
    $metaData = $fs->getMetadata($fileName);
    $stream = $fs->readStream($fileName);
    
    if (ob_get_level()) ob_end_clean();
    
    return response()->stream(
        function () use ($stream) {
            fpassthru($stream);
        },
        200,
        [
            'Content-Type' => $metaData['type'],
            'Content-disposition' => 'attachment; filename="' . $metaData['path'] . '"',
        ]);
    

提交回复
热议问题