设为首页 收藏本站
查看: 440|回复: 0

[经验分享] 【收藏】基于Nginx XSendfile+SpringMVC进行文件下载

[复制链接]

尚未签到

发表于 2016-12-27 10:04:42 | 显示全部楼层 |阅读模式
Java代码  
@RequestMapping("/courseware/{id}")   
public void download(@PathVariable("id") String courseID, HttpServletResponse response) throws Exception {   
  
     ResourceFile file = coursewareService.downCoursewareFile(courseID);   
     response.setContentType(file.getType());   
     response.setContentLength(file.contentLength());   
     response.setHeader("Content-Disposition","attachment; filename=\"" + file.getFilename() +"\"");   
     //Reade File - > Write To response   
     FileCopyUtils.copy(file.getFile(), response.getOutputStream());   
}  


   @RequestMapping("/courseware/{id}")
   public void download(@PathVariable("id") String courseID, HttpServletResponse response) throws Exception {

        ResourceFile file = coursewareService.downCoursewareFile(courseID);
        response.setContentType(file.getType());
        response.setContentLength(file.contentLength());
        response.setHeader("Content-Disposition","attachment; filename=\"" + file.getFilename() +"\"");
        //Reade File - > Write To response
        FileCopyUtils.copy(file.getFile(), response.getOutputStream());
    }
    由于程序的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代码  
server {   
        listen 80;   
        server_name dl.mydomain.com;   
  
        location / {   
            proxy_pass  http://127.0.0.1:8080/;  #首先pass到应用服务器   
            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;   
  
            client_max_body_size       10m;   
            client_body_buffer_size    128k;   
  
            proxy_connect_timeout      90;   
            proxy_send_timeout         90;   
            proxy_read_timeout         90;   
  
            proxy_buffer_size          4k;   
            proxy_buffers              4 32k;   
            proxy_busy_buffers_size    64k;   
            proxy_temp_file_write_size 64k;   
  
        }   
  
        location /course/ {   
            charset utf-8;   
            alias       /nfs/files/; #文件的根目录(允许使用本地磁盘,NFS,NAS,NBD等)   
            internal;   
        }   
    }  

server {
        listen 80;
        server_name dl.mydomain.com;

        location / {
            proxy_pass  http://127.0.0.1:8080/;  #首先pass到应用服务器
            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;

            client_max_body_size       10m;
            client_body_buffer_size    128k;

            proxy_connect_timeout      90;
            proxy_send_timeout         90;
            proxy_read_timeout         90;

            proxy_buffer_size          4k;
            proxy_buffers              4 32k;
            proxy_busy_buffers_size    64k;
            proxy_temp_file_write_size 64k;

        }

        location /course/ {
            charset utf-8;
            alias       /nfs/files/; #文件的根目录(允许使用本地磁盘,NFS,NAS,NBD等)
            internal;
        }
    }

    其Spring代码为:
    Java代码  
package com.xxxx.portal.web;   
  
import java.io.IOException;   
import java.io.UnsupportedEncodingException;   
  
import javax.servlet.http.HttpServletResponse;   
  
import org.springframework.beans.factory.annotation.Autowired;   
import org.springframework.stereotype.Controller;   
import org.springframework.web.bind.annotation.PathVariable;   
import org.springframework.web.bind.annotation.RequestMapping;   
  
import com.xxxx.core.io.ResourceFile;   
import com.xxxx.portal.services.CoursewareService;   
  
/**  
* File download controller, provide courseware download or other files. <br>  
* <br>  
* <i> download a course URL e.g:<br>  
* http://dl.mydomain.com/download/courseware/1 </i>  
*   
* @author denger  
*/  
@Controller  
@RequestMapping("/download/*")   
public class DownloadController {   
  
    private CoursewareService coursewareService;   
      
    protected static final String DEFAULT_FILE_ENCODING = "ISO-8859-1";   
  
    /**   
     * Under the courseware id to download the file.   
     *   
     * @param courseID The course id.   
     * @throws IOException   
     */   
    @RequestMapping("/courseware/{id}")   
    public void downCourseware(@PathVariable("id") String courseID, final HttpServletResponse response) throws IOException {   
        ResourceFile file = coursewareService.downCoursewareFile(courseID);   
        if (file != null && file.exists()){   
            // redirect file to x-accel-Redirect   
            xAccelRedirectFile(file, response);   
  
        } else { // If not found resource file, send the 404 code   
            response.sendError(404);   
        }   
    }   
  
    protected void xAccelRedirectFile(ResourceFile file, HttpServletResponse response)   
        throws IOException {   
        String encoding = response.getCharacterEncoding();   
  
        response.setHeader("Content-Type", "application/octet-stream");   
        //这里获取到文件的相对路径。其中 /course/ 为虚拟路径,主要用于nginx中进行拦截包含了/course/ 的URL, 并进行文件下载。   
        //在以上nginx配置的第二个location 中同样也设置了 /course/,实际的文件下载路径并不会包含 /course/   
        //当然,如果希望包含的话可以将以上的 alias 改为 root 即可。   
        response.setHeader("X-Accel-Redirect", "/course/"  
                + toPathEncoding(encoding, file.getRelativePath()));   
        response.setHeader("X-Accel-Charset", "utf-8");   
  
        response.setHeader("Content-Disposition", "attachment; filename="  
                + toPathEncoding(encoding, file.getFilename()));   
        response.setContentLength((int) file.contentLength());   
    }   
  
    //如果存在中文文件名或中文路径需要对其进行编码成 iSO-8859-1   
    //否则会导致 nginx无法找到文件及弹出的文件下载框也会乱码   
    private String toPathEncoding(String origEncoding, String fileName) throws UnsupportedEncodingException{   
        return new String(fileName.getBytes(origEncoding), DEFAULT_FILE_ENCODING);   
    }   
  
    @Autowired  
    public void setCoursewareService(CoursewareService coursewareService) {   
        this.coursewareService = coursewareService;   
    }   
}  

package com.xxxx.portal.web;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.xxxx.core.io.ResourceFile;
import com.xxxx.portal.services.CoursewareService;

/**
* File download controller, provide courseware download or other files. <br>
* <br>
* <i> download a course URL e.g:<br>
* http://dl.mydomain.com/download/courseware/1 </i>
*
* @author denger
*/
@Controller
@RequestMapping("/download/*")
public class DownloadController {

private CoursewareService coursewareService;

protected static final String DEFAULT_FILE_ENCODING = "ISO-8859-1";

/**
* Under the courseware id to download the file.
*
* @param courseID The course id.
* @throws IOException
*/
@RequestMapping("/courseware/{id}")
public void downCourseware(@PathVariable("id") String courseID, final HttpServletResponse response) throws IOException {
ResourceFile file = coursewareService.downCoursewareFile(courseID);
if (file != null && file.exists()){
// redirect file to x-accel-Redirect
xAccelRedirectFile(file, response);

} else { // If not found resource file, send the 404 code
response.sendError(404);
}
}

protected void xAccelRedirectFile(ResourceFile file, HttpServletResponse response)
throws IOException {
String encoding = response.getCharacterEncoding();

response.setHeader("Content-Type", "application/octet-stream");
        //这里获取到文件的相对路径。其中 /course/ 为虚拟路径,主要用于nginx中进行拦截包含了/course/ 的URL, 并进行文件下载。
        //在以上nginx配置的第二个location 中同样也设置了 /course/,实际的文件下载路径并不会包含 /course/
        //当然,如果希望包含的话可以将以上的 alias 改为 root 即可。
response.setHeader("X-Accel-Redirect", "/course/"
+ toPathEncoding(encoding, file.getRelativePath()));
response.setHeader("X-Accel-Charset", "utf-8");

response.setHeader("Content-Disposition", "attachment; filename="
+ toPathEncoding(encoding, file.getFilename()));
response.setContentLength((int) file.contentLength());
}

    //如果存在中文文件名或中文路径需要对其进行编码成 iSO-8859-1
    //否则会导致 nginx无法找到文件及弹出的文件下载框也会乱码
private String toPathEncoding(String origEncoding, String fileName) throws UnsupportedEncodingException{
return new String(fileName.getBytes(origEncoding), DEFAULT_FILE_ENCODING);
}

@Autowired
public void setCoursewareService(CoursewareService coursewareService) {
this.coursewareService = coursewareService;
}
}

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-320073-1-1.html 上篇帖子: Nginx学习总结:proxy与rewrite模块(三) 下篇帖子: Nginx+Apache搭建前后端web生产环境[转]
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表