工作中碰到使用Java下载文件的情况,对一些特殊情况纠结了很久,总算解决了,记录下对下载问题的探究过程以及中间碰到的坑。
一、使用response下载
使用response对象的正常下载
response.reset(); response.setHeader("Content-Disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8")); response.setHeader("Connection", "close"); response.setHeader("Content-Type", "application/octet-stream"); OutputStream ops = null; FileInputStream fis =null; byte[] buffer = new byte[8192]; int bytesRead = 0; try { ops = response.getOutputStream(); fis = new FileInputStream(path); while((bytesRead = fis.read(buffer, 0, 8192)) != -1){ ops.write(buffer, 0, bytesRead); } ops.flush(); } catch (IOException e) { e.printStackTrace(); } finally { try { if(fis != null){ fis.close(); } if(ops != null){ ops.close(); } } catch (IOException e) { e.printStackTrace(); } }
二、多个文件下载的情况
开始想使用response进行多个文件的下载,在后端处理,结果无法实现。
坑一:一个request只能返回对应的一个response
因此,无法把多个文件使用response对象进行下载。如果不关闭ops,强行输出到一个文件中,则文件会不可读。使用Excel恢复可读,也只有第一个文件存在。
后来就想尝试发送多个请求,就使用Ajax尝试多次发送请求。
坑二:不能使用Ajax请求下载文件
因为Ajax的返回类型是json、text、html、xml等类型,不是流类型,所以无法实现文件下载。
所以,只能使用隐藏表单或者iframe的方法。
隐藏表单方法:
存在的问题是提交时会全部加到一起,只提交一次。jQuery('<form action="'+url+'" method="post">' + '<input type="text" name="filename" value="'+filename+'"/>' + '<input type="text" name="filetype" value="'+filetype+'"/>' + '</form>').appendTo('body').submit();
最终解决方法:
页面添加多个隐藏iframe,循环设置src,分别load
三、Ajax请求实现文件下载方法
1、使用隐藏表单,提交隐藏表单
2、使用window.open()或window.location.href()方法
3、使用隐藏iframe,src指向后端需要的地址
四、Ajax无法进行文件下载,但仍可以获得文件内容,文件将被保留在内存中,无法保存到磁盘。因为JavaScript无法与磁盘进行交互,js无法调用到浏览器的下载处理机制和程序,会被浏览器阻塞。
五、response的getWriter()方法在连续输出两次流到页面的时候,第二次的流会包括第一次的流,所以需用response.reset()或response.resetBuffer()方法来重置
response.reset()
Clears any data that exists in the buffer as well as the status code and headers. If the response has been committed, this method throws an IllegalStateException.
response.reset()的使用有一个条件受限:response的任何打开流关闭之后都不能再reset
resetBuffer()
Clears the content of the underlying buffer in the response without clearing headers or status code. If the response has been committed, this method throws an
IllegalStateException
可以看到resetBuffer方法与reset方法的区别是,头和状态码没有清除
六、多个文件下载还可以通过将多个文件压缩到一个压缩包再行下载