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解决前端访问资源跨域问题的方法详解
Mar 31 Servers
Nginx服务器添加Systemd自定义服务过程解析
Mar 31 Servers
Nginx 根据URL带的参数转发的实现
Apr 01 Servers
Nginx配置文件详解以及优化建议指南
Sep 15 Servers
图文详解Nginx版本平滑升级方案
Sep 15 Servers
详解nginx安装过程并代理下载服务器文件
Feb 12 Servers
Nginx实现会话保持的两种方式
Mar 18 Servers
nginx常用配置conf的示例代码详解
Mar 21 Servers
了解Kubernetes中的Service和Endpoint
Apr 01 Servers
CentOS安装Nginx并部署vue
Apr 12 Servers
vscode内网访问服务器的方法
Jun 28 Servers
nginx访问报403错误的几种情况详解
Jul 23 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
德生PL550的电路分析
2021/03/02 无线电
在同一窗体中使用PHP来处理多个提交任务
2006/10/09 PHP
php自动给文章加关键词链接的函数代码
2012/11/29 PHP
浅析PHP绘图技术
2013/07/03 PHP
greybox——不开新窗口看新的网页
2007/02/20 Javascript
JS获取url链接字符串 location.href
2013/12/23 Javascript
js函数定时器实现定时读取系统实时连接数
2014/04/30 Javascript
JS替换字符串中空格方法
2015/04/17 Javascript
JavaScript Math.round() 方法
2015/12/18 Javascript
js无法获取到html标签的属性的解决方法
2016/07/26 Javascript
jquery 抽奖小程序实现代码
2016/10/12 Javascript
基于bootstrap风格的弹框插件
2016/12/28 Javascript
原生JS实现《别踩白块》游戏(兼容IE)
2017/02/20 Javascript
COM组件中调用JavaScript函数详解及实例
2017/02/23 Javascript
Vue异步组件使用详解
2017/04/08 Javascript
VsCode插件整理(小结)
2017/09/14 Javascript
浅谈 Vue 项目优化的方法
2017/12/16 Javascript
Vue cli3 库模式搭建组件库并发布到 npm的流程
2018/10/12 Javascript
vue本地打开build后生成的dist文件夹index.html问题
2019/09/04 Javascript
实例分析JS中的相等性判断===、 ==和Object.is()
2019/11/17 Javascript
JS document内容及样式操作完整示例
2020/01/14 Javascript
[01:30:54]《加油DOTA》 第三期
2014/08/18 DOTA
Python快速从注释生成文档的方法
2016/12/26 Python
Python探索之Metaclass初步了解
2017/10/28 Python
Python使用matplotlib填充图形指定区域代码示例
2018/01/16 Python
Django 拆分model和view的实现方法
2019/08/16 Python
Python3.9又更新了:dict内置新功能
2020/02/28 Python
QML实现钟表效果
2020/06/02 Python
HttpServlet类中的主要方法都有哪些?各自的作用是什么?
2014/03/16 面试题
阿德的梦教学反思
2014/02/06 职场文书
2015年全国“爱牙日”宣传活动总结
2015/03/23 职场文书
演讲开场白台词大全
2015/05/29 职场文书
惊天动地观后感
2015/06/10 职场文书
证婚人婚礼致辞
2015/07/28 职场文书
暑假生活随笔
2015/08/15 职场文书
Vue的过滤器你真了解吗
2022/02/24 Vue.js