一尘不染

在WebApp中创建和下载巨大的ZIP(从多个BLOB)的最佳实践

java

我将需要从Web应用程序中下载大量文件。

显然,这将是一项长期运行的操作( 每年[每个客户] 使用 一次
),因此时间不是问题(除非遇到一些超时,但是我可以通过创建某种形式的Keepalive心跳)。我知道如何创建一个隐藏的 iframe
并使用它content-disposition: attachment来尝试下载文件,而不是在浏览器中打开它,以及如何为绘制进度表而建立客户端与服务器之间的通信;

下载的实际大小(和文件数)是未知的,但是为简单起见,我们可以将其实际上视为1GB,由100个文件组成,每个文件10MB。

由于这应该是一次单击操作,因此我首先想到的是将所有文件分组,同时从数据库中读取它们,并以动态生成的ZIP形式进行存储,然后要求用户保存ZIP。

问题是: 从WebApp中的多个小字节数组创建大型存档时,最佳实践是什么?已知的缺点和陷阱是什么?

可以随机分为:

  • 应该将每个字节数组转换为物理临时文件,还是可以将它们添加到内存中的ZIP中?
  • 如果是,我知道我必须处理名称的可能相等(它们可以在数据库的不同记录中具有相同的名称,但不能在同一文件系统或ZIP中):还有其他可能的问题吗?介意(假设文件系统始终具有足够的物理空间)?
  • 由于我不能依靠足够的RAM来执行内存中的全部操作,因此我猜应该先将ZIP创建并馈送到文件系统,然后再发送给用户。有什么方法可以做不同的事情(例如,使用 websocket ),例如询问用户将文件保存在何处,然后启动从服务器到客户端的恒定数据流(我猜是 科幻 )?
  • 您会想到的任何其他相关已知问题或最佳做法将不胜感激。

阅读 162

收藏
2020-09-08

共1个答案

一尘不染

对于无法立即放入内存的大型内容,请将内容从数据库 流式传输 到响应。

这种事情实际上很简单。您不需要AJAX或websocket,可以通过用户单击的简单链接来流式传输大文件下载。而且现代的浏览器都有不错的下载管理器,它们都有自己的进度条-
为什么要重新发明轮子?

如果为此从头开始编写servlet,请访问数据库BLOB,获取其输入流,然后将内容复制到HTTP响应输出流。如果您具有Apache Commons
IO库,则可以使用IOUtils.copy(),否则您可以自己执行此操作。

可以使用ZipOutputStream快速创建ZIP文件。在响应输出流上(从servlet或框架提供的任何内容)创建一个,然后从数据库中获取每个BLOB,putNextEntry()首先使用,然后如前所述对每个BLOB进行流式处理。

潜在的陷阱/问题:

  • 根据下载大小和网络速度,请求可能需要很多时间才能完成。防火墙等可以阻止这种情况并尽早终止请求。
  • 希望您的用户在请求这些文件时处于良好的公司网络中。如果是远程/躲闪/移动连接,情况会更糟(如果下载了2.0G的1.9G后掉线了,用户必须重新启动)。
  • 它可能会给您的服务器带来一些负担,尤其是压缩巨大的ZIP文件时。ZipOutputStream如果这是一个问题,则可能值得在创建时关闭/关闭压缩功能。
  • 2GB以上(或4 GB)的ZIP文件可能与某些ZIP程序有关。我认为最新的Java 7使用ZIP64扩展,因此此版本的Java将正确编写巨大的ZIP,但是客户端是否将具有支持大zip文件的程序?我以前肯定遇到过这些问题,尤其是在旧的Solaris服务器上
2020-09-08