• 基于 Nginx XSendfile + SpringMVC 进行文件下载


    转自:http://denger.iteye.com/blog/1014066

    基于 Nginx XSendfile + SpringMVC 进行文件下载

    PS:经过实际测试,通过 nginx 提供文件下载功能的时候,在 Application Server(Java/RoR/Go...) 端不设置 Content-Length 也是可以的

    在平常我们实现文件下载通常是通过普通 read-write方式,如下代码所示。

    Java代码  收藏代码
    1. @RequestMapping("/courseware/{id}")   
    2. public void download(@PathVariable("id") String courseID, HttpServletResponse response) throws Exception {  
    3.   
    4.      ResourceFile file = coursewareService.downCoursewareFile(courseID);  
    5.      response.setContentType(file.getType());  
    6.      response.setContentLength(file.contentLength());  
    7.      response.setHeader("Content-Disposition","attachment; filename="" + file.getFilename() +""");  
    8.      //Reade File - > Write To response  
    9.      FileCopyUtils.copy(file.getFile(), response.getOutputStream());  
    10.  }  


        由于程序的IO都是调用系统底层IO进行文件操作,于是这种方式在read和write时系统都会进行两次内存拷贝(共四次)。linux 中引入的 sendfile 的实际就为了更好的解决这个问题,从而实现"零拷贝",大大提升文件下载速度。
        使用 sendfile() 提升网络文件发送性能
        RoR网站如何利用lighttpd的X-sendfile功能提升文件下载性能
      

        在apache,nginx,lighttpd等web服务器当中,都有sendfile feature。下面就对 nginx 上的XSendfile与SpringMVC文件下载及访问控制进行说明。我们这里的大体流程为:
         1.用户发起下载课件请求; (http://dl.mydomain.com/download/courseware/1)
         2.nginx截获到该(dl.mydomain.com)域名的请求;
         3.将其proxy_pass至应用服务器;
         4.应用服务器根据课件id获取文件存储路径等其它一些业务逻辑(如增加下载次数等);
         5.如果允许下载,则应用服务器通过setHeader -> X-Accel-Redirect 将需要下载的文件转发至nginx中);
         6.Nginx获取到header以sendfile方式从NFS读取文件并进行下载


         其nginx中的配置为:
         在location中加入以下配置
         

    Conf代码  收藏代码
    1. server {  
    2.         listen 80;  
    3.         server_name dl.mydomain.com;  
    4.   
    5.         location / {  
    6.             proxy_pass  http://127.0.0.1:8080/;  #首先pass到应用服务器  
    7.             proxy_redirect     off;  
    8.             proxy_set_header   Host             $host;  
    9.             proxy_set_header   X-Real-IP        $remote_addr;  
    10.             proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;  
    11.   
    12.             client_max_body_size       10m;  
    13.             client_body_buffer_size    128k;  
    14.   
    15.             proxy_connect_timeout      90;  
    16.             proxy_send_timeout         90;  
    17.             proxy_read_timeout         90;  
    18.   
    19.             proxy_buffer_size          4k;  
    20.             proxy_buffers              4 32k;  
    21.             proxy_busy_buffers_size    64k;  
    22.             proxy_temp_file_write_size 64k;  
    23.   
    24.         }  
    25.   
    26.         location /course/ {   
    27.             charset utf-8;  
    28.             alias       /nfs/files/; #文件的根目录(允许使用本地磁盘,NFS,NAS,NBD等)  
    29.             internal;  
    30.         }  
    31.     }  



        其Spring代码为:
       

    Java代码  收藏代码
    1. package com.xxxx.portal.web;  
    2.   
    3. import java.io.IOException;  
    4. import java.io.UnsupportedEncodingException;  
    5.   
    6. import javax.servlet.http.HttpServletResponse;  
    7.   
    8. import org.springframework.beans.factory.annotation.Autowired;  
    9. import org.springframework.stereotype.Controller;  
    10. import org.springframework.web.bind.annotation.PathVariable;  
    11. import org.springframework.web.bind.annotation.RequestMapping;  
    12.   
    13. import com.xxxx.core.io.ResourceFile;  
    14. import com.xxxx.portal.services.CoursewareService;  
    15.   
    16. /** 
    17.  * File download controller, provide courseware download or other files. <br> 
    18.  * <br> 
    19.  * <i> download a course URL e.g:<br> 
    20.  * http://dl.mydomain.com/download/courseware/1 </i> 
    21.  *  
    22.  * @author denger 
    23.  */  
    24. @Controller  
    25. @RequestMapping("/download/*")  
    26. public class DownloadController {  
    27.   
    28.     private CoursewareService coursewareService;  
    29.       
    30.     protected static final String DEFAULT_FILE_ENCODING = "ISO-8859-1";  
    31.   
    32.     /**  
    33.      * Under the courseware id to download the file.  
    34.      *   
    35.      * @param courseID The course id.  
    36.      * @throws IOException   
    37.      */  
    38.     @RequestMapping("/courseware/{id}")  
    39.     public void downCourseware(@PathVariable("id") String courseID, final HttpServletResponse response) throws IOException {  
    40.         ResourceFile file = coursewareService.downCoursewareFile(courseID);  
    41.         if (file != null && file.exists()){  
    42.             // redirect file to x-accel-Redirect   
    43.             xAccelRedirectFile(file, response);  
    44.   
    45.         } else { // If not found resource file, send the 404 code  
    46.             response.sendError(404);  
    47.         }  
    48.     }  
    49.   
    50.     protected void xAccelRedirectFile(ResourceFile file, HttpServletResponse response)   
    51.         throws IOException {  
    52.         String encoding = response.getCharacterEncoding();  
    53.   
    54.         response.setHeader("Content-Type", "application/octet-stream");  
    55.         //这里获取到文件的相对路径。其中 /course/ 为虚拟路径,主要用于nginx中进行拦截包含了/course/ 的URL, 并进行文件下载。  
    56.         //在以上nginx配置的第二个location 中同样也设置了 /course/,实际的文件下载路径并不会包含 /course/  
    57.         //当然,如果希望包含的话可以将以上的 alias 改为 root 即可。  
    58.         response.setHeader("X-Accel-Redirect", "/course/"  
    59.                 + toPathEncoding(encoding, file.getRelativePath()));  
    60.         response.setHeader("X-Accel-Charset", "utf-8");  
    61.   
    62.         response.setHeader("Content-Disposition", "attachment; filename="  
    63.                 + toPathEncoding(encoding, file.getFilename()));  
    64.         // response.setContentLength((int) file.contentLength());  // 经过实际测试,这里不设置 Content-Length 也是可以的
    65.     }  
    66.   
    67.     //如果存在中文文件名或中文路径需要对其进行编码成 iSO-8859-1  
    68.     //否则会导致 nginx无法找到文件及弹出的文件下载框也会乱码  
    69.     private String toPathEncoding(String origEncoding, String fileName) throws UnsupportedEncodingException{  
    70.         return new String(fileName.getBytes(origEncoding), DEFAULT_FILE_ENCODING);  
    71.     }  
    72.   
    73.     @Autowired  
    74.     public void setCoursewareService(CoursewareService coursewareService) {  
    75.         this.coursewareService = coursewareService;  
    76.     }  
  • 相关阅读:
    Python学习笔记(三): 收集参数
    Effective Java 之-----关于延迟初始化
    Effective Java 之-----返回零长度的数组或集合而不是null
    CSS学习笔记(一):定位与溢出
    Python学习笔记(二):字典
    Effective Java 之-----静态工厂与构造器
    Effective Java 之-----for-each循环优于传统的for循环
    Python学习笔记(一):列表和元组
    Effective Java 之-----精确的答案与double&float
    Effective Java 之-----消除过期的对象引用
  • 原文地址:https://www.cnblogs.com/leoncfor/p/4754426.html
Copyright © 2020-2023  润新知