zoukankan      html  css  js  c++  java
  • 基于 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.     }  
  • 相关阅读:
    How to alter department in PMS system
    Can't create new folder in windows7
    calculate fraction by oracle
    Long Wei information technology development Limited by Share Ltd interview summary.
    ORACLE BACKUP AND RECOVERY
    DESCRIBE:When you mouse click right-side is open an application and click left-side is attribution.
    ORACLE_TO_CHAR Function
    电脑BOIS设置
    JSP点击表头排序
    jsp+js实现可排序表格
  • 原文地址:https://www.cnblogs.com/leoncfor/p/4754426.html
Copyright © 2011-2022 走看看