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的问题
02 将文件流通过循环写入到相应输出流中(推荐)
- @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);
- }
- }
复制代码 03 从网络上获取文件并返回给前端
- @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);
- }
-
- }
复制代码 04 从网络上获取文本并下载到本地
- @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);
- }
- }
复制代码 05 总结
一定要搞清晰InputStream和OutputStream的区别,如果搞不清晰的,可以和字符流进行映射,InputStream -> Reader,OutPutStream -> Writer,换成这样你就知道读取内容需要使用Reader,写入需要使用Writer了。
返回给前端的是输出流,不需要你显示的去返回(return response;),这样会报错
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |