SpringBoot返回文件使前端下载的几种方式小结
01 背景
在后端开发中,通常会有文件下载的需求,常用的解决方案有两种:
- 不通过后端应用,直接使用
nginx
直接转发文件地址下载(适用于一些公开的文件,因为这里不需要授权) - 通过后端进行下载,同时进行一些业务处理
本篇主要以方法2
进行介绍,方法2
的原理步骤如下:
- 读取文件,得到文件的字节流
- 将字节流写入到响应输出流中
02 一次性读取到内存,通过响应输出流输出到前端
@GetMapping("/file/download") public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) { File file = new File(filePath); if (!file.exists()) { throw new BusinessException("当前下载的文件不存在,请检查路径是否正确"); } // 将文件写入输入流 try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) { // 一次性读取到内存中 byte[] buffer = new byte[is.available()]; int read = is.read(buffer); // 清空 response response.reset(); response.setCharacterEncoding("UTF-8"); // Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存 // attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3" // filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称 response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8")); // 告知浏览器文件的大小 response.addHeader("Content-Length", "" + file.length()); OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); response.setContentType("application/octet-stream"); outputStream.write(buffer); outputStream.flush(); outputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } }
适用于小文件,如果文件过大,一次性读取到内存中可能会出现oom的问题
03 将文件流通过循环写入到响应输出流中(推荐)
@GetMapping("/file/download") public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) { File file = new File(filePath); if (!file.exists()) { throw new BusinessException("当前下载的文件不存在,请检查路径是否正确"); } // 清空 response response.reset(); response.setCharacterEncoding("UTF-8"); response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8")); response.setContentType("application/octet-stream"); // 将文件读到输入流中 try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) { OutputStream outputStream = new BufferedOutputStream(response.getOutputStream()); byte[] buffer = new byte[1024]; int len; //从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1 while((len = is.read(buffer)) > 0){ outputStream.write(buffer, 0, len); } outputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } }
04 从网络上获取文件并返回给前端
@GetMapping("/net/download") public void netDownload(HttpServletResponse response, @RequestParam("fileAddress") String fileAddress, @RequestParam("filename") String filename) { try { URL url = new URL(fileAddress); URLConnection conn = url.openConnection(); InputStream inputStream = conn.getInputStream(); response.reset(); response.setContentType(conn.getContentType()); response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8")); byte[] buffer = new byte[1024]; int len; OutputStream outputStream = response.getOutputStream(); while ((len = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, len); } inputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } }
05 从网络上获取文本并下载到本地
@GetMapping("/netDownloadLocal") public void downloadNet(@RequestParam("netAddress") String netAddress, @RequestParam("filepath") String filepath) { try { URL url = new URL(netAddress); URLConnection conn = url.openConnection(); InputStream inputStream = conn.getInputStream(); FileOutputStream fileOutputStream = new FileOutputStream(filepath); int byteread; byte[] buffer = new byte[1024]; while ((byteread = inputStream.read(buffer)) != -1) { fileOutputStream.write(buffer, 0, byteread); } fileOutputStream.close(); } catch (IOException e) { throw new RuntimeException(e); } }
06 总结
一定要搞清楚 InputStream
和OutputStream
的区别,如果搞不清楚的,可以和字符流进行映射,InputStream -> Reader
,OutPutStream -> Writer
,换成这样你就知道读取内容需要使用Reader
,写入需要使用Writer
了。
返回给前端的是输出流,不需要你显示的去返回(return response;
),这样会报错
到此这篇关于SpringBoot返回文件使前端下载的几种方式小结的文章就介绍到这了,更多相关SpringBoot返回文件下载内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
springboot(thymeleaf)中th:field和th:value的区别及说明
这篇文章主要介绍了springboot(thymeleaf)中th:field和th:value的区别及说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-10-10使用IDEA如何打包发布SpringBoot并部署到云服务器
这篇文章主要介绍了使用IDEA如何打包发布SpringBoot并部署到云服务器问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2023-12-12Java中Integer的parseInt和valueOf的区别详解
这篇文章主要介绍了Java中Integer的parseInt和valueOf的区别详解,nteger.parseInt(s)是把字符串解析成int基本类型,Integer.valueOf(s)是把字符串解析成Integer对象类型,其实int就是Integer解包装,Integer就是int的包装,需要的朋友可以参考下2023-11-11mybatis-plus 自定义 Service Vo接口实现数据库实体与 vo
这篇文章主要介绍了mybatis-plus 自定义 Service Vo接口实现数据库实体与 vo 对象转换返回功能,本文通过实例图文相结合给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧2024-08-08解决@CachePut设置的key值无法与@CacheValue的值匹配问题
这篇文章主要介绍了解决@CachePut设置的key的值无法与@CacheValue的值匹配问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2021-12-12JavaEE组件commons-fileupload实现文件上传、下载
这篇文章主要介绍了JavaEE组件commons-fileupload实现文件上传、下载,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2016-10-10
最新评论