如果你想把它做好,单靠 PHP 是做不到的。您可能希望使用 Nginx 的 X-Accel-Redirect (推荐) 或 Apache 的 X-Sendfile 来提供文件,它们正是为此目的而构建的。
我将在此答案中包含一些在此 article 上找到的文本。
为什么不用 PHP 提供文件:
- 天真地完成,文件被读入内存,然后被提供。如果
文件很大,这可能会导致您的服务器内存不足。
- 缓存标头通常设置不正确。这会导致网络浏览器
即使文件没有更改,也要多次重新下载文件。
- 通常不支持 HEAD 请求和范围请求
自动支持。如果文件很大,则提供此类文件
绑定一个工作进程或线程。这可能会导致饥饿,如果
可用的工人有限。增加工人数量
可能会导致服务器内存不足。
NGINX 可以正确处理所有这些事情。因此,让我们在应用程序中处理权限检查,并让 NGINX 为实际文件提供服务。这就是内部重定向的用武之地。这个想法很简单:您可以在提供常规文件时像往常一样配置位置条目。
将此添加到您的 nginx 服务器块:
location /protected_files/ {
internal;
alias /var/www/my_folder_with_protected_files/;
}
在您的项目中,需要 HTTP Foundation 包:
composer require symfony/http-foundation
使用 Nginx 在 PHP 中提供文件:
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;
这应该是您入门所需的基本知识。
这是一个提供内联 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;