PHP 搭配 Nginx 之 X-Accel-Redirect 响应文件

老牛浏览 7评论 0发表于 更新于

一、前言

在实际开发中,经常遇到需要用户上传文件的情况,如何保存文件以及读取文件就需要我们考虑考虑了。

对于公开文件,比如博客图片,一般我们会直接存放在公共位置,所有人都可以访问。对于私密文件,例如设计图纸,或者照片等,我们通常会单独放在一个位置,访问时进行鉴权,只允许指定人查看。

在 Laravel 中存储文件时,就本地磁盘而言,默认就有 localpublic 两个磁盘。local 非公开,无法直接访问;而 public 对应的文件可直接通过 URL 地址访问。

非公开的文件如何让用户访问到呢?这就是今天要介绍的 Nginx 中的 X-Accel-Redirect

二、问题

使用 PHP 返回文件有如下三个问题:

  • 内存占用高:PHP 需要将整个文件读入内存(不考虑流式传输)

  • 效率低下:特别是对于大文件传输场景

  • 扩展性差:难以应对高并发场景

X-Accel-Redirect 通过将文件传输任务从 PHP 移交给 Nginx 完美解决了这些问题。

三、解决方案

3.1 X-Accel-Redirect

a. Nginx 配置

nginx
.
.
.
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";
}
.
.
.

b. Laravel 控制器实现

php
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'
    ]);
}

c. 智能限速策略

nginx
limit_rate_after 500k;  # 前 500KB 全速
limit_rate 200k;        # 之后限速 200KB/s

3.2 流式响应

除了 Nginx 方案,也可以使用流,在 Laravel 中实现如下:

php
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 鉴权的方式(判断签名)来实现。

点赞
收藏
暂无评论,快来发表评论吧~
私信
老牛@ilaoniu
老牛,俗称哞哞。单纯的九零后理工小青年。喜欢折腾,爱玩,爱音乐,爱游戏,爱电影,爱旅游...
最后活跃于