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 反向代理之 proxy_pass的实现
Mar 31 Servers
Nginx解决403 forbidden的完整步骤
Apr 01 Servers
Nginx location 和 proxy_pass路径配置问题小结
Sep 04 Servers
Nginx工作模式及代理配置的使用细节
Mar 21 Servers
Nginx虚拟主机的配置步骤过程全解
Mar 31 Servers
Docker下安装Oracle19c
Apr 13 Servers
如何通过cmd 连接阿里云服务器
Apr 18 Servers
Apache Hudi 加速传统的批处理模式
Apr 24 Servers
使用Nginx+Tomcat实现负载均衡的全过程
May 30 Servers
使用 DataAnt 监控 Apache APISIX的原理解析
Jul 07 Servers
win7配置本地ftp服务器的图文教程
Aug 05 Servers
SpringBoot前端后端分离之Nginx服务器下载安装过程
Aug 14 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
10个可以简化php开发过程的MySQL工具
2010/04/11 PHP
解析php开发中的中文编码问题
2013/08/08 PHP
codeigniter教程之上传视频并使用ffmpeg转flv示例
2014/02/13 PHP
phpmyadmin出现Cannot start session without errors问题解决方法
2014/08/14 PHP
laravel dingo API返回自定义错误信息的实例
2019/09/29 PHP
JavaScript中的数组特性介绍
2014/12/30 Javascript
JavaScript中的null和undefined区别介绍
2015/01/01 Javascript
JS实现可直接显示网页代码运行效果的HTML代码预览功能实例
2015/08/06 Javascript
Java Mybatis框架入门基础教程
2015/09/21 Javascript
JavaScript实现倒计时代码段Item1(非常实用)
2015/11/03 Javascript
javascript简单实现等比例缩小图片的方法
2016/07/27 Javascript
AngularJs Understanding the Model Component
2016/09/02 Javascript
javascript实现根据函数名称字符串动态执行函数的方法示例
2016/12/28 Javascript
JS复制对应id的内容到粘贴板(Ctrl+C效果)
2017/01/23 Javascript
js实现无缝滚动图
2017/02/22 Javascript
Bootstrap的popover(弹出框)在append后弹不出(失效)
2017/02/27 Javascript
canvas绘图不清晰的解决方案
2017/02/28 Javascript
Vue学习笔记进阶篇之过渡状态详解
2017/07/14 Javascript
React进阶学习之组件的解耦之道
2017/08/07 Javascript
js闭包学习心得总结
2018/04/17 Javascript
JavaScript中为事件指定处理程序的五种方式分析
2018/07/27 Javascript
Vue监听一个数组id是否与另一个数组id相同的方法
2018/09/26 Javascript
vue+axios+promise实际开发用法详解
2018/10/15 Javascript
JavaScript学习笔记之DOM基础操作实例小结
2019/01/09 Javascript
详解vue-cli3 中跨域解决方案
2019/04/10 Javascript
说说Vuex的getters属性的具体用法
2019/04/15 Javascript
vue路由守卫,限制前端页面访问权限的例子
2019/11/11 Javascript
[01:53]2016完美“圣”典风云人物:Maybe专访
2016/12/05 DOTA
python处理html转义字符的方法详解
2016/07/01 Python
Python 爬虫之超链接 url中含有中文出错及解决办法
2017/08/03 Python
美国大城市最热门旅游景点门票:CityPASS
2016/12/16 全球购物
优秀员工年终发言演讲稿
2014/01/01 职场文书
乡镇消防工作实施方案
2014/03/27 职场文书
在Python中如何使用yield
2021/06/07 Python
springboot @ConfigurationProperties和@PropertySource的区别
2021/06/11 Java/Android
详解flex:1什么意思
2022/07/23 HTML / CSS