Serving large files with PHP

前端 未结 9 975
北海茫月
北海茫月 2020-12-13 07:51

So I am trying to serve large files via a PHP script, they are not in a web accessible directory, so this is the best way I can figure to provide access to them.

The

9条回答
  •  一整个雨季
    2020-12-13 08:16

    If you want to do it right, PHP alone can't do it. You would want to serve the file by using Nginx's X-Accel-Redirect (Recommended) or Apache's X-Sendfile, which are built exactly for this purpose.

    I will include in this answer some text found on this article.

    Why not serve the files with PHP:

    • Done naively, the file is read into memory and then served. If the files are large, this could cause your server to run out of memory.
    • Caching headers are often not set correctly. This causes web browsers to re-download the file multiple times even if it hasn't changed.
    • Support for HEAD requests and range requests is typically not automatically supported. If the files are large, serving such files ties up a worker process or thread. This can lead to starvation if there are limited workers available. Increasing the number of workers can cause your server to run out of memory.

    NGINX handles all of these things properly. So let's handle permission checks in the application and let NGINX serve the actual file. This is where internal redirects come in. The idea is simple: you can configure a location entry as usual when serving regular files.

    Add this to your nginx server block:

    location /protected_files/ {
        internal;
        alias /var/www/my_folder_with_protected_files/;
    }
    

    In your project, require the HTTP Foundation package:

    composer require symfony/http-foundation

    Serve the files in PHP using Nginx:

    use Symfony\Component\HttpFoundation\BinaryFileResponse;
    
    $real_path = '/var/www/my_folder_with_protected_files/foo.pdf';
    $x_accel_redirect_path = '/protected_files/foo.pdf';
    
    BinaryFileResponse::trustXSendfileTypeHeader();
    $response = new BinaryFileResponse( $real_path );
    $response->headers->set( 'X-Accel-Redirect', $accel_file );
    $response->sendHeaders();
    exit;
    

    This should be the basic you need to get started.

    Here's a more complete example serving an Inline PDF:

    use Symfony\Component\HttpFoundation\BinaryFileResponse;
    use Symfony\Component\HttpFoundation\File\File;
    use Symfony\Component\HttpFoundation\ResponseHeaderBag;
    
    $real_path = '/var/www/my_folder_with_protected_files/foo.pdf';
    $x_accel_redirect_path = '/protected_files/foo.pdf';
    
    $file = new File( $file_path );
    
    BinaryFileResponse::trustXSendfileTypeHeader();
    $response = new BinaryFileResponse( $file_path );
    $response->setImmutable( true );
    $response->setPublic();
    $response->setAutoEtag();
    $response->setAutoLastModified();
    $response->headers->set( 'Content-Type', 'application/pdf' );
    $response->headers->set( 'Content-Length', $file->getSize() );
    $response->headers->set( 'X-Sendfile-Type', 'X-Accel-Redirect' );
    $response->headers->set( 'X-Accel-Redirect', $accel_file );
    $response->headers->set( 'X-Accel-Expires', 60 * 60 * 24 * 90 ); // 90 days
    $response->headers->set( 'X-Accel-Limit-Rate', 10485760 ); // 10mb/s
    $response->headers->set( 'X-Accel-Buffering', 'yes' );
    $response->setContentDisposition( ResponseHeaderBag::DISPOSITION_INLINE, basename( $file_path ) ); // view in browser. Change to DISPOSITION_ATTACHMENT to download
    $response->sendHeaders();
    exit;
    

提交回复
热议问题