今天分享一个我在一个客户现场碰到的问题。
因为客户那边的文件都比较大,在现场他们从服务器提取文件的流程又很麻烦,并且不允许使用ftp这种形式。所以只能通过流的形式慢慢往出读取。从一开始就碰到了问题,前端会报
504 Gateway Time-out
针对这一个问题,我们只能通过调整请求响应时间的方式。因为是持续通过流的形式往response写,很耗费时间,文件很大的话就会超时。刚好我们的前端是通过nginx部署,解决方案很多,修改几个配置即可
改完配置接着测试,传统的通过通道流下载方法如下:
private void downloadLargeFile(final File file, final String resultFileName, final HttpServletResponse response) { OutputStream os = null; try { response.reset(); response.setContentType("application/x-msdownload"); response.setHeader("Content-disposition", "attachment; filename=" + (StringUtils.isBlank(resultFileName) ? file.getName() : resultFileName)); os = response.getOutputStream(); final String path = Files.probeContentType(Paths.get(file.getAbsolutePath())); log.info("path的值为------------------" + path); final FileInputStream in = new FileInputStream(file); final WritableByteChannel writableByteChannel = Channels.newChannel(os); final FileChannel fileChannel = in.getChannel(); fileChannel.transferTo(0, fileChannel.size(), writableByteChannel); fileChannel.close(); os.flush(); } catch (final IOException e) { e.printStackTrace(); } finally { if (os != null) { try { os.close(); } catch (final IOException e) { e.printStackTrace(); } } } }
这总方式比普通流要速度更快,但是FileChannel.transferTo()方法对于2G以上的文件也只有2G
解决方法也很简单,只需要把下载方法改造一下(把fileChannel.transferTo方法替换为下述代码)
//文件大小
long size = fileChannel.size();
//偏移量
long position = 0;
while (position < fileChannel.size()) {
//通道最多进入2048MB
long count = fileChannel.transferTo(position,size,writableByteChannel);
if (count>0) {
position+=count;
size-=count;
}
}
//fileChannel.transferTo(0, fileChannel.size(), writableByteChannel);
这样就可以解决2G的限制了。但也不是多大就可以,受到请求时间的限制,以及客户端临时文件空间限制(因为在客户端下载的时候,客户端浏览器首先会把response流写入本地硬盘上的临时文件中),经过我的电脑测试,10G左右的文件还是可以下载的。