SpringBoot返回文件让前端下载的几种方式

打印 上一主题 下一主题

主题 840|帖子 840|积分 2520

01 背景

在后端开发中,通常会有文件下载的需求,常用的解决方案有两种:

  • 不通过后端应用,直接使用nginx直接转发文件地址下载(适用于一些公开的文件,由于这里不需要授权)
  • 通过后端进行下载,同时进行一些业务处置惩罚
本篇主要以方法2进行介绍,方法2的原理步骤如下:

  • 读取文件,得到文件的字节流
  • 将字节流写入到相应输出流中
02 一次性读取到内存,通过相应输出流输出到前端

  1.     @GetMapping("/file/download")
  2.     public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) {
  3.         File file = new File(filePath);
  4.         if (!file.exists()) {
  5.             throw new BusinessException("当前下载的文件不存在,请检查路径是否正确");
  6.         }
  7.         // 将文件写入输入流
  8.         try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
  9.             
  10.             // 一次性读取到内存中
  11.             byte[] buffer = new byte[is.available()];
  12.             int read = is.read(buffer);
  13.             // 清空 response
  14.             response.reset();
  15.             response.setCharacterEncoding("UTF-8");
  16.             // Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
  17.             // attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"
  18.             // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
  19.             response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
  20.             // 告知浏览器文件的大小
  21.             response.addHeader("Content-Length", "" + file.length());
  22.             OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
  23.             response.setContentType("application/octet-stream");
  24.             outputStream.write(buffer);
  25.             outputStream.flush();
  26.             outputStream.close();
  27.         } catch (IOException e) {
  28.             throw new RuntimeException(e);
  29.         }
  30.     }
复制代码
  适用于小文件,如果文件过大,一次性读取到内存中大概会出现oom的问题
  02 将文件流通过循环写入到相应输出流中(推荐)

  1.     @GetMapping("/file/download")
  2.     public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) {
  3.         File file = new File(filePath);
  4.         if (!file.exists()) {
  5.             throw new BusinessException("当前下载的文件不存在,请检查路径是否正确");
  6.         }
  7.         // 清空 response
  8.         response.reset();
  9.         response.setCharacterEncoding("UTF-8");
  10.         response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
  11.         response.setContentType("application/octet-stream");
  12.         // 将文件读到输入流中
  13.         try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {
  14.             
  15.             OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
  16.             
  17.             byte[] buffer = new byte[1024];
  18.             int len;
  19.             //从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1
  20.             while((len = is.read(buffer)) > 0){
  21.                 outputStream.write(buffer, 0, len);
  22.             }
  23.             outputStream.close();
  24.         } catch (IOException e) {
  25.             throw new RuntimeException(e);
  26.         }
  27.     }
复制代码
03 从网络上获取文件并返回给前端

  1.     @GetMapping("/net/download")
  2.     public void netDownload(HttpServletResponse response, @RequestParam("fileAddress") String fileAddress, @RequestParam("filename") String filename) {
  3.         try {
  4.             URL url = new URL(fileAddress);
  5.             URLConnection conn = url.openConnection();
  6.             InputStream inputStream = conn.getInputStream();
  7.             response.reset();
  8.             response.setContentType(conn.getContentType());
  9.             response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
  10.             byte[] buffer = new byte[1024];
  11.             int len;
  12.             
  13.             OutputStream outputStream = response.getOutputStream();
  14.             
  15.             while ((len = inputStream.read(buffer)) > 0) {
  16.                 outputStream.write(buffer, 0, len);
  17.             }
  18.             
  19.             inputStream.close();
  20.             
  21.         } catch (IOException e) {
  22.             throw new RuntimeException(e);
  23.         }
  24.         
  25.     }
复制代码
04 从网络上获取文本并下载到本地

  1.     @GetMapping("/netDownloadLocal")
  2.     public void downloadNet(@RequestParam("netAddress") String netAddress, @RequestParam("filepath") String filepath) {
  3.         try {
  4.             URL url = new URL(netAddress);
  5.             URLConnection conn = url.openConnection();
  6.             InputStream inputStream = conn.getInputStream();
  7.             FileOutputStream fileOutputStream = new FileOutputStream(filepath);
  8.             int byteread;
  9.             byte[] buffer = new byte[1024];
  10.             while ((byteread = inputStream.read(buffer)) != -1) {
  11.                 fileOutputStream.write(buffer, 0, byteread);
  12.             }
  13.             fileOutputStream.close();
  14.         } catch (IOException e) {
  15.             throw new RuntimeException(e);
  16.         }
  17.     }
复制代码
05 总结

一定要搞清晰InputStream和OutputStream的区别,如果搞不清晰的,可以和字符流进行映射,InputStream -> Reader,OutPutStream -> Writer,换成这样你就知道读取内容需要使用Reader,写入需要使用Writer了。
返回给前端的是输出流,不需要你显示的去返回(return response;),这样会报错

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万万哇

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表