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返回文件下载内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论