Nginx使用X-Accel-Redirect实现静态文件下载的统计、鉴权、防盗链、限速等


Posted in Servers onApril 04, 2021

需求

  • 统计静态文件的下载次数;
  • 判断用户是否有下载权限;
  • 根据用户指定下载速度;
  • 根据Referer判断是否需要防盗链;
  • 根据用户属性限制下载速度;

X-Accel-Redirect

This allows you to handle authentication, logging or whatever else you please in your backend and then have NGINX handle serving the contents from redirected location to the end user, thus freeing up the backend to handle other requests. This feature is commonly known as X-Sendfile.
这个功能允许你在后端处理权限,日志或任何你想干的,Nginx提供内容服务给终端用户从重定向后的路径,因此可以释放后端去处理其他请求(直接由Nginx提供IO,而不是后端服务)。这个功能类似 X-Sendfile 。

不同 Web 服务器,相同功能,不同的标识:

nginx: X-Accel-Redirect 
squid: X-Accelerator-Vary 
apache: X-Sendfile 
lighttpd: X-Sendfile/X-LIGHTTPD-send-file 

X-Accel-Limit-Rate

限制下载速度,单位字节。默认不限速度。

X-Accel-Buffering

设置此连接的代理缓存,将此设置为no将允许适用于Comet和HTTP流式应用程序的无缓冲响应。将此设置为yes将允许响应被缓存。默认yes。

X-Accel-Expires

如果已传输过的文件被缓存下载,设置Nginx文件缓存过期时间,单位秒。默认不过期。

X-Accel-Charset

设置文件字符集,默认utf-8。

使用条件

  • 必须有Nginx作为后端服务的代理;
  • 必须访问Nginx的代理地址,直接访问后端服务Nginx会报404;
  • 可自行配置Content-Type来控制是下载(application/octet-stream)还是展示(image/jpeg等);

代码实现

  1. Nginx监听15555端口。
  2. Nginx代理后端服务的18000端口。
  3. 设置/file路径为internal,指定具体文件存储的磁盘位置。
  4. 后端服务接收到文件下载请求,处理业务逻辑后X-Accel-Redirect/file路径。
  5. Nginx收到后端返回信息中的X-Accel-Redirect请求头,接管文件下载任务。
  6. 请求路径:http://localhost:15555/f/1.jpg

Java-SpringMVC版本

Nginx配置

server {
    listen 15555;

    location / {
        proxy_redirect off;
        proxy_set_header Host  $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:18000/;            
    }

    location /file {
        internal;
        alias /data/file;
    }
}

Java代码

注意fileName添加:.+,或者获取不到文件后缀名。

@GetMapping(value = "/f/{fileName:.+}")
public void file(@PathVariable String fileName, HttpServletResponse response, HttpServletRequest request) throws IOException {
    //统计
    //鉴权
    //判断Referer
    String referer = request.getHeader("Referer");
    if (referer == null || !referer.startsWith("https://www.zhangbj.com")) {
        response.sendError(403, "Forbidden");
        return;
    }
    response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
    response.setHeader("Content-Type", "application/octet-stream");
    response.setHeader("X-Accel-Redirect","/file/" + fileName);
    response.setHeader("X-Accel-Limit-Rate","1024");//限速,单位字节,默认不限
    response.setHeader("X-Accel-Buffering","yes");//是否使用Nginx缓存,默认yes
}

PHP-ThinkPHP版本

Nginx配置

server {
    listen  80;
    server_name  localhost;
    root   D:/z-blog/public;
    location / {
        index  index.html index.htm index.php;
        if (!-e $request_filename) {
            rewrite  ^(.*)$  /index.php?s=/$1  last;
            break;
        }
    }

    location ~ \.php(.*)$ {
        fastcgi_pass   127.0.0.1:9500;
        fastcgi_index  index.php;
        fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        fastcgi_param  PATH_INFO  $fastcgi_path_info;
        fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
        include        fastcgi_params;
    }

    location /file {
        internal;
        alias D:/file;
    }
}

ThinkPHP代码

Router:

Route::get('/f/:fileName', 'index/File/file');

Controller:

<?php

namespace app\index\controller;

use think\Request;

class File {
    public function file($fileName) {
        $request = Request::instance();
        $referer = $request->header('referer');
        if (!$referer || strpos($referer, 'https://www.zhangbj.com') !== 0) {
            header("Status:403 Forbidden");
            return;
        }
        header('Content-type: application/octet-stream');
        header("Content-Disposition: attachment; filename=$fileName");
        header("X-Accel-Redirect:  /file/$fileName");
    }
}

参考

Nginx: X-Accel-Redirect

Nginx: XSendfile

Servers 相关文章推荐
详解nginx进程锁的实现
Jun 14 Servers
centos8安装nginx1.9.1的详细过程
Aug 02 Servers
Nginx缓存设置案例详解
Sep 15 Servers
Apache Pulsar集群搭建部署详细过程
Feb 12 Servers
docker-compose部署Yapi的方法
Apr 08 Servers
Nginx+Tomcat负载均衡多实例详解
Apr 11 Servers
CentOS7安装GlusterFS集群以及相关配置
Apr 12 Servers
Nginx 常用配置
May 15 Servers
linux目录管理方法介绍
Jun 01 Servers
Win2008系统搭建DHCP服务器
Jun 25 Servers
Flink 侧流输出源码示例解析
Sep 23 Servers
ubuntu开机后ROS程序自启动问题
Dec 24 Servers
Nginx工作原理和优化总结。
利用Nginx代理如何解决前端跨域问题详析
Apr 02 #Servers
Nginx URL重写rewrite机制原理及使用实例
Apr 01 #Servers
nginx限制并发连接请求数的方法
Apr 01 #Servers
Nginx已编译的nginx-添加新模块
Nginx下配置Https证书详细过程
详解Nginx启动失败的几种错误处理
Apr 01 #Servers
You might like
php5中date()得出的时间为什么不是当前时间的解决方法
2008/06/30 PHP
php 常用类汇总 推荐收藏
2010/05/13 PHP
PHP计算2点经纬度之间的距离代码
2013/08/12 PHP
thinkphp模板赋值与替换实例简述
2014/11/24 PHP
javascript 子窗体父窗体相互传值方法
2010/05/31 Javascript
jquery实现textarea输入框限制字数的方法
2015/01/15 Javascript
详解JavaScript节流函数中的Throttle
2016/07/16 Javascript
web前端开发upload上传头像js示例代码
2016/10/22 Javascript
jQuery实现在新增加的元素上添加事件方法案例分析
2017/02/09 Javascript
jQuery访问浏览器本地存储cookie、localStorage和sessionStorage的基本用法
2017/10/20 jQuery
js原生方法被覆盖,从新赋值原生的方法
2018/01/02 Javascript
JS基于Location实现访问Url、重定向及刷新页面的方法分析
2018/12/03 Javascript
关于vue项目中搜索节流的实现代码
2019/09/17 Javascript
VuePress 中如何增加用户登录功能
2019/11/29 Javascript
[02:49]DAC2018决赛日TOP5 LGD开启黑暗之门绝杀VP
2018/04/08 DOTA
[01:18:33]Secret vs VGJ.S Supermajor小组赛C组 BO3 第一场 6.3
2018/06/04 DOTA
[09:34]2018DOTA2国际邀请赛寻真——永不放弃的iG
2018/08/14 DOTA
使用Python判断IP地址合法性的方法实例
2014/03/13 Python
python求crc32值的方法
2014/10/05 Python
利用python发送和接收邮件
2016/09/27 Python
python 调用c语言函数的方法
2017/09/29 Python
VSCode下好用的Python插件及配置
2018/04/06 Python
Python实现的维尼吉亚密码算法示例
2018/04/12 Python
Python使用一行代码获取上个月是几月
2018/08/30 Python
python微信聊天机器人改进版(定时或触发抓取天气预报、励志语录等,向好友推送)
2019/04/25 Python
python实现KNN分类算法
2019/10/16 Python
“型”走纽约上东区:Sam Edelman
2017/04/02 全球购物
幼儿园校车司机的岗位职责
2014/01/30 职场文书
九年级英语教学反思
2014/01/31 职场文书
晨会主持词
2014/03/17 职场文书
毕业生自荐信如何写
2014/03/24 职场文书
产品推广策划方案
2014/05/10 职场文书
公司备用金管理制度
2015/08/04 职场文书
MYSQL 无法识别中文的永久解决方法
2021/06/03 MySQL
Redis 操作多个数据库的配置的方法实现
2022/03/23 Redis
Vue OpenLayer 为地图绘制风场效果
2022/04/24 Vue.js