在实际开发中,经常遇到需要用户上传文件的情况,如何保存文件以及读取文件就需要我们考虑考虑了。
对于公开文件,比如博客图片,一般我们会直接存放在公共位置,所有人都可以访问。对于私密文件,例如设计图纸,或者照片等,我们通常会单独放在一个位置,访问时进行鉴权,只允许指定人查看。
在 Laravel 中存储文件时,就本地磁盘而言,默认就有 local
和 public
两个磁盘。local
非公开,无法直接访问;而 public
对应的文件可直接通过 URL 地址访问。
非公开的文件如何让用户访问到呢?这就是今天要介绍的 Nginx 中的 X-Accel-Redirect
。
使用 PHP 返回文件有如下三个问题:
内存占用高:PHP 需要将整个文件读入内存(不考虑流式传输)
效率低下:特别是对于大文件传输场景
扩展性差:难以应对高并发场景
X-Accel-Redirect
通过将文件传输任务从 PHP 移交给 Nginx 完美解决了这些问题。
X-Accel-Redirect
.
.
.
location /protected-files/ {
internal; # 仅允许内部请求
alias /path/to/storage/app/private/files/; # 指向 local 磁盘路径
# 性能优化
sendfile on;
tcp_nopush on;
# 缓存控制
expires 30d;
add_header Cache-Control "public, immutable";
# 安全设置
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
}
.
.
.
public function stream($fileId)
{
$file = File::findOrFail($fileId);
if (!auth()->user()->can('access', $file)) {
abort(403);
}
return response()->noContent(200, [
'X-Accel-Redirect' => "/protected-files/{$audio->path}",
'Content-Type' => Storage::mimeType($file->path),
'Cache-Control' => 'public, max-age=2592000'
]);
}
limit_rate_after 500k; # 前 500KB 全速
limit_rate 200k; # 之后限速 200KB/s
除了 Nginx 方案,也可以使用流,在 Laravel 中实现如下:
public function stream($fileId)
{
$file = File::findOrFail($fileId);
if (!auth()->user()->can('access', $file)) {
abort(403);
}
// 方法1:直接返回文件流(自动处理缓存头)
return response()->file(storage_path('app/' . $file->path));
// 方法2:使用 Storage Facade(推荐,适配不同磁盘)
return Storage::disk('local')->response($file->path);
}
以上两种方案根据实际需要选择即可。
当然,如果文件不是存在本地,而是存放在 OSS 中,也可以通过 URL 鉴权的方式(判断签名)来实现。