Java文件下载挂起

我有一个用于下载文件的Web界面。 当请求进入时,我的glassfish服务器从Web服务流式传输文件,然后将内容写入输出流。 我的代码工作正常,除非文件大小变得非常大(如超过200 MB),它挂起显示0%已下载到浏览器中,文件永远不会下载。

当我在while循环中移动flush()方法时,它也适用于大文件。 我不确定在循环中放置flush()是否有问题。 不知道这件事实际上是如何运作的。 我的代码如下:

HttpURLConnection conn = (HttpURLConnection) downloadUri.toURL().openConnection(); conn.setDoOutput(true); conn.setRequestMethod("GET"); conn.setRequestProperty("Content-Type", "application/pdf"); if (conn.getResponseCode() == 200) { ServletOutputStream output; try (InputStream inputStream = conn.getInputStream()) { HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse(); response.setContentType("application/octet-stream"); response.setHeader("Content-Length", conn.getHeaderField("Content-Length")); response.setHeader("Content-Disposition", "attachment; filename=\"" + abbr + ".pdf\""); output = response.getOutputStream(); byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { output.write(buffer, 0, bytesRead); } } output.flush(); output.close(); 

有什么想法吗?。 感谢您对此进行调查。

flush()方法指示流实际沿流管道发送输出。

出于各种性能原因,各种流实现可以缓存输出而不是立即写入底层流。

例如,从性能的角度来看,在磁盘上保存IO操作是昂贵的。

刷新流没有问题,如果不是为了表演,在这种情况下就是你想要的:流似乎被卡住直到你冲洗它,所以你希望它实际上把东西发送到客户端。

也许你可以玩大小超过1024的缓冲区大小,看看哪个更合适。

编辑:

在循环中冲洗或不在循环中冲洗的问题相对不相关。

您可以随时调用flush,因为它将调用底层操作系统流,无论这是性能影响还是不依赖于情况。

例如,您可以估计200 MB的ram,其中流缓冲文件比IO操作更重要,也是性能方面的。

或者更简单地重视用户体验,即如果您设法测量,实际下载的文件比您可能遇到的最终性能影响更重要。

如上所述,缓冲区越大,循环问题越少。 假设,作为一个极端的例子,你的缓冲区是100兆字节,那么一个80兆字节的文件将只获得一个刷新,它会在请求结束时得到它。

拥有1k的缓冲区可能太小,4k更好,16k就好了,这是IO调用和RAM消耗之间的权衡。

流应该自己做正确的工作,如果你看到一个200MB的文件被完全缓存,除非你调用flush,那么显然流可能是优化性能但是给用户带来了不好的体验,所以显然你需要它在循环中。

正如大卫所说,刷新强制下载通过,而不是将所有未写入的数据保存在内存中。

在循环中刷新本身并不是问题,但是您可能不希望每次循环运行时都要刷新。 请确保在刷新之前不要写太多(如200MB)数据。

编辑: jtahlborn提出了一个很好的观点。 BufferedWriter自动刷新。 这里的另一个答案更好,但对于那些好奇的人来说, 这就是你写的时候会发生的事情!

这是BufferedWriterpublic void write(char, int, int)的源代码。 你可以在第182行看到,它会在达到缓冲区大小后自动刷新( 默认为8,192 )