文件下载

时间 2019/11/10 17:54:18 加载中...

文件下载

碰到文件下载通常是这两种业务情况:

Web端下载文件
Web端查询列表导出EXCEL

输出流

文件下载都是通过 页面响应的 输出流 来实现的
不同编程语言输出流的名字可能不同。

在 Java 的 Spring Boot 项目中,在控制器上可以通过 HttpServletResponse 来获取输出流 ServletOutputStream。

ServletOutputStream outputStream = response.getOutputStream();

输出流输出到页面的是 byte数组

页面输出字符串

Java 中字符串可以通过 getBytes() 方法转化成对应的 byte 数组
byte[] byteArr = ("标题,内容\n").getBytes();

因此,我们可以通过 ServletOutputStream 来输出字符串

  1. @GetMapping("output/string")
  2. private void outputStr(HttpServletResponse response){
  3. try {
  4. ServletOutputStream outputStream = response.getOutputStream();
  5. byte[] data = "abc".getBytes();
  6. outputStream.write(data);
  7. } catch (IOException e) {
  8. e.printStackTrace();
  9. }
  10. }

效果如下:

页面输出文件

页面输出文件,就是将文件以 byte 数组的形式输出,这里我输出的是 TestClass.java 文件

  1. /*一次性将文件所有字节读取出来,并输出出去*/
  2. @GetMapping("file/outputfile")
  3. public void outputfile(HttpServletRequest request, HttpServletResponse response) {
  4. try {
  5. ServletOutputStream outputStream = response.getOutputStream();
  6. File file = new File("/Users/adminqian/shen/TestClass.java");
  7. FileInputStream inputStream = new FileInputStream(file);
  8. /*一次性读取文件的所有字节内容*/
  9. int length = inputStream.available();
  10. byte[] data = new byte[length];
  11. inputStream.read(data);
  12. outputStream.write(data);
  13. /*如果文件过大,会直接提示 java.lang.OutOfMemoryError: Requested array size exceeds VM limit */
  14. outputStream.flush();
  15. outputStream.close();
  16. } catch (IOException e) {
  17. log(e.toString());
  18. }
  19. }

展示的效果是:

如果文件是一个压缩包呢,效果就是下面的样子,由于压缩包不是文本文件,所以输出的字节数组就成了乱码。

页面下载文件

输出文件内容并不是我们的业务需求,我们要下载文件,又该如何处理呢?
我们只需设置 HTTP 响应的响应头即可。

  1. String name = "12.iso";
  2. response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
  3. response.setHeader("Content-Disposition", "attachment; filename=" + name);

通过设置响应头的 Content-Type 和 Content-Disposition 即可。
Content-Type 设置为 application/octet-stream;charset=utf-8 即内容输出的是二进制。
Content-Disposition 设置为 attachment 即输出的是文件,文件名字为后面的 filename。

问题:输出的文件名乱码
原因是在不同浏览器下,编码不同导致的。可以判断浏览器类型,来使用不同的编码。

  1. if (request.getHeader("User-Agent") != null) {
  2. String agent = request.getHeader("User-Agent").toLowerCase();
  3. //识别IE浏览器
  4. if (agent != null && (agent.indexOf("msie") != -1 ||
  5. (agent.indexOf("rv") != -1 && agent.indexOf("firefox") == -1))) {
  6. name = URLEncoder.encode(name, "UTF-8");
  7. } else {
  8. name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
  9. }
  10. }

输出流分块输出

不管是输出字符串,还是文件,我们都是一次性输出了数据。
即调用了一次 outputStream.write(data); 方法。
如果 data 特别大,而可能会出现问题。所以建议是通过循环的形式,一块一块的输出。

比如:

  1. @GetMapping("file/output")
  2. public void getFile(HttpServletRequest request, HttpServletResponse response) {
  3. try {
  4. String name = "12.csv";
  5. response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
  6. response.setHeader("Content-Disposition", "attachment; filename=" + name);
  7. ServletOutputStream outputStream = response.getOutputStream();
  8. byte[] byteArr = ("标题,内容\n").getBytes();
  9. outputStream.write(byteArr);
  10. for (int i = 0; i < 10000 * 100; i++) {
  11. outputStream.write(String.format("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s\n", i, i + 1, i + 2, i + 3, i + 4, i + 5, i + 6, i + 7, i + 8, i + 9).getBytes());
  12. }
  13. outputStream.flush();
  14. outputStream.close();
  15. } catch (IOException e) {
  16. log(e.toString());
  17. }
  18. }
  19. /*将文件一部分一部分的读取,然后一部分一部分的输出到浏览器*/
  20. @GetMapping("file/output2")
  21. public void getFile2(HttpServletRequest request, HttpServletResponse response) {
  22. try {
  23. String name = "12.iso";
  24. response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
  25. response.setHeader("Content-Disposition", "attachment; filename=" + name);
  26. ServletOutputStream outputStream = response.getOutputStream();
  27. File file = new File("/Users/adminqian/shen/cn_windows_7_enterprise_x64_dvd_x15-70741.iso");
  28. FileInputStream inputStream = new FileInputStream(file);
  29. byte[] chunk = new byte[1024];
  30. int result;
  31. do {
  32. result = inputStream.read(chunk);
  33. outputStream.write(chunk);
  34. }
  35. while (result != -1);
  36. outputStream.flush();
  37. outputStream.close();
  38. } catch (IOException e) {
  39. log(e.toString());
  40. }
  41. }

大文件下载的建议

首先,大文件输出肯定要采用分块输出的形式。

另外,不管是直接将字符串输出,还是读取已有文件,都可以实现文件的下载。
因此我们可以根据实际情况来采用不同的方法。

比如,我们把数据库中的数据导出来这样的业务场景,我们就可以直接拼成以逗号分隔的字符串。
然后输出出来。
注意不要先将数据保存到磁盘文件上,然后将文件再输出出来。这样肯定会影响效率。
有时候,从数据库中查找出来的记录,我们并不完全放在文件中,也不要再遍历数据,
生成新的集合数据,直接遍历输出的时候,有选择性的输出即可。

现有类库

HttpHelper 查看

下载接口文件/下载网络文件

  1. //浏览器下载文件
  2. /* 将第三方图片在自己页面上展示,(url地址如果能直接用更好)*/
  3. /* 将接口获取的文件输出到浏览器 */
  4. @GetMapping("file")
  5. public void httpDownloadFile(HttpServletResponse response) throws IOException {
  6. String url = "http://oimagec6.ydstatic.com/image?id=7347104849285270631&product=dict-homepage&w=&h=&fill=0&cw=&ch=&sbc=0&cgra=CENTER&of=jpeg";
  7. //response.setContentLengthLong(contentLength);
  8. response.setHeader("Content-Type","image/jpeg");
  9. ServletOutputStream outputStream = response.getOutputStream();
  10. HttpHelper.downloadFile(url, null, outputStream);
  11. }
  12. // 浏览器下载文件,从服务器下载文件
  13. @GetMapping("/file/get")
  14. public void get(String filePath, String displayName, String type, HttpServletRequest request, HttpServletResponse response) throws IOException {
  15. File image = new File(filePath);
  16. FileInputStream inputStream = new FileInputStream(image);
  17. int length = inputStream.available();
  18. byte data[] = new byte[length];
  19. inputStream.read(data);
  20. String name = image.getName();
  21. if (StringUtil.isNotBlank(displayName))
  22. name = displayName;
  23. if (request.getHeader("User-Agent") != null) {
  24. String agent = request.getHeader("User-Agent").toLowerCase();
  25. //识别IE浏览器
  26. if (agent != null && (agent.indexOf("msie") != -1 ||
  27. (agent.indexOf("rv") != -1 && agent.indexOf("firefox") == -1))) {
  28. name = URLEncoder.encode(name, "UTF-8");
  29. } else if (agent != null && agent.indexOf("firefox") != -1) {
  30. name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
  31. } else {
  32. //name = new String(name.getBytes("UTF-8"), "ISO-8859-1");
  33. name = URLEncoder.encode(name, "UTF-8");
  34. }
  35. }
  36. response.setContentLength(length);
  37. if(type != null && type.equalsIgnoreCase("html")){
  38. response.setHeader("Content-Type", "text/html;");
  39. }else{
  40. response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
  41. response.setHeader("Content-Disposition", "attachment; filename=" + name);
  42. }
  43. OutputStream toClient = response.getOutputStream();
  44. toClient.write(data);
  45. toClient.flush();
  46. }
  47. //保存文件到本地
  48. public static void saveFileToLocal() throws IOException {
  49. String url = "http://oimagec6.ydstatic.com/image?id=7347104849285270631&product=dict-homepage&w=&h=&fill=0&cw=&ch=&sbc=0&cgra=CENTER&of=jpeg";
  50. HttpHelper.downloadFile(url, null, "e:/abc.png");
  51. }
扫码分享
版权说明
作者:SQBER
文章来源:http://www.sqber.com/articles/download-file.html
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。