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!


    1. @RequestMapping("/download")
    2. public StreamingResponseBody handle() {
    3.     return new StreamingResponseBody() {
    4.         @Override
    5.         public void writeTo(OutputStream outputStream) throws IOException {
    6.             // write...
    7.         }
    8.     };
    9. }

    第二种方法



  • @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;
  • }



    转载地址

    http://www.crazyit.org/thread-12193-1-1.html

  • @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();
  •                 }
  •         }
  • }
  • @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;
  • }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值