疯狂java


您现在的位置: 疯狂软件 >> 新闻资讯 >> 正文

SpringMVC不使用Servlet API实现文件下载


 

 
是否通过SpringMVC下载文件,其实原理是一样的,无非就是要设置3个响应头:
Content-Length : 下载的内容长度,以字节计算
Content-Type    : 内容类型,使用MIME-Type(多用途互联网邮件扩展类型)
Content-Disposition : 处理方式
 
 
其中Content-Disposition响应头在不同的浏览器上面处理还是不同的,所以需要特别处理!有下面两种主要方式:
attachment; filename=FILE_NAME   其中FILE_NAME是用URLEncoder编码后的文件名,用UTF-8的编码。支持IE的文件名处理。
attachment; filename*=UTF-8'FILE_NAME 这种方式可以指定FILE_NAME的字符编码,其中FILE_NAME也是要用URLEncoder进行编码!支持非
IE浏览器。
 
 
特别两个问题:
使用非IE浏览器,并且通过*='方式指定字符编码的时候,必须要确保filename后面有个*,并且在字符编码后面必须跟着两个单引号!格式非常重要哦,否则浏览器不认识的。
文件下载的时候,URL最好不要有中文名。通常来讲,文件上传到服务器以后,不会直接把文件名给用户的,而是把文件的ID给用户;下载时通过文件的ID来找到实际的文件信息。
 
 
ResponseEntity对象,是SpringMVC里面完全的自定义响应体,包括响应码、响应头、响应体都可以自定义。这里就是利用ResponseEntity实现文件的下载,这种方式脱离了Servlet的API,但是也对程序做出了限制:文件不能超过2G、下载大文件时需要较大内存。
 
@RequestMapping(value = "/{xx}", method = RequestMethod.GET)
public ResponseEntity<byte[]> download(//
                @PathVariable("xx") String id, // 文件id
                @RequestHeader("User-Agent") String userAgent// 浏览器版本
    ) throws UnsupportedEncodingException {
        // 根据文件的id获取文件信息,里面包括文件大小、中文文件名、文件的内容类型
        FileInfo info = this.fileService.getById(id);
        if (info == null) {
                // 404
                log.error("无法根据路径找到对应的文件信息");
                return ResponseEntity.notFound().build();
        } else {
                // 构建响应消息
                // ok() 其实就是 HTTP 200 响应
                BodyBuilder builder = ResponseEntity.ok();
                builder.contentLength(info.getFileLength());// 内容长度
                builder.contentType(//
                                MediaType.parseMediaType(info.getContentType())// 内容类型
                );
                // 获得实际的文件名,实际的开发中需要根据不同的浏览器来进行判断做不同的编码
                // 实际的浏览器类型可以通过请求头来获取
                String name = info.getName();
                name = URLEncoder.encode(name, "UTF-8");
                // 设置实际的响应文件名,告诉浏览器文件要用于【下载】、【保存】
                // 不同的浏览器,处理方式不同,要根据浏览器版本进行区别判断
                if (userAgent.indexOf("MSIE") > 0) {
                        // 如果是IE,只需要用UTF-8字符集进行URL编码即可
                        builder.header("Content-Disposition", "attachment; filename=" + name);
                } else {
                        // 而Google、FireFox、Chrome等浏览器,则需要说明编码的字符集
                        // 注意filename后面有个*号,在UTF-8后面有两个单引号!
                        builder.header("Content-Disposition", "attachment; filename*=UTF-8'" + name);
                }
                // 根据实际的文件路径得到文件,并且转换为byte[]
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                try (FileInputStream in = new FileInputStream(info.getPath())) {
                        // 把输入流里面的信息,读取出来转换为byte[]
                        byte[] buf = new byte[1024];
                        for (int count = in.read(buf); count != -1; count = in.read(buf)) {
                                out.write(buf, 0, count);
                        }
                        byte[] data = out.toByteArray();
                        // 构建响应体
                        ResponseEntity<byte[]> entity = builder.body(data);
                        return entity;
                } catch (IOException e) {
                        log.error("找到了对应的文件信息,但是读取文件内容失败:" + e.getLocalizedMessage(), e);
                        // 文件没有找到,或者读取失败
                        return ResponseEntity.notFound().build();
                }
        }
}
 
有问题,请接着看后面的回帖!
 
 
 
 
 
如果下载的文件大于2G怎么办?如果不想要占用那么大的内存怎么办?SpringMVC已经提出了非常完美的解决方案,那就是:StreamingResponseBody!请继续看下面两个回帖!
 
luo_wenqiang
 
3#
  楼主| 发表于 昨天 22:14 | 只看该作者
@RequestMapping("/download")
public StreamingResponseBody handle() {
    return new StreamingResponseBody() {
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
            // write...
        }
    };
}
复制代码
 
还能够这样干:
@RequestMapping(value = "/{xx}", method = RequestMethod.GET)
public ResponseEntity<StreamingResponseBody> download(//
                @PathVariable("xx") String id, // 文件id
                @RequestHeader("User-Agent") String userAgent// 浏览器版本
    ) throws UnsupportedEncodingException {
 
    BodyBuilder builder = ResponseEntity.ok();
    builder.contentLength(info.getFileLength());// 内容长度
    builder.contentType(//
        MediaType.parseMediaType(info.getContentType())// 内容类型
    );
 
    //
    StreamingResponseBody body = new StreamingResponseBody(){
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
            // write...
        }
    };
 
    ResponseEntity<byte[]> entity = builder.body(body);
    return entity;
}
复制代码