使用Java恢复失败的压缩
我有一个很小的数字(2-10)的大文件(6-15GB),压缩得非常好(4:1)。使用Java恢复失败的压缩
我正在用Java编写客户端和服务器,并且我想将这些文件从客户端发送到服务器,以便 1.客户端压缩发送文件时(即没有中间的.zip文件被创建)
2.服务器上的压缩内容最终成为格式良好的文件(例如.zip或.tgz文件),以便可以按原样下载。
3.转移可以,如果它通过
4.故障中途恢复转移在新的进程完全
前两个可以很容易地使用java.io插座和java实现的可能发生的恢复。 util.zip ZipOutputStreams。第三个是让我悲伤的那个。第四个只是上下文。
我猜测一个解决方案可能需要某种部分重新传输或重新解析来建立一个字典,或者什么的。
是否有任何支持可恢复压缩的Java库?
我找不到任何以我需要的方式支持可恢复压缩的预制造库。然而,许多公开许可证可用于编写自己的零件。我现在有了一个客户/服务器解决方案,可以满足问题中列出的所有限制。
这个想法类似于上面概述的组块想法,但服务器管理分块并做一些簿记,将客户端上的压缩块映射到服务器上的压缩块。解决方案中没有任何临时文件。基本方案如下
(1) The client sends a manifest to the server, containing the to-be contents of the zip file (2) The server sends back an ID for the manifest Then repeatedly (3) The client asks the server "is there anything still required for the manifest with ID X" (4) The server replies "no", or with a manifest entry for a file in the manifest, plus a offset and length to send (5) The client compresses that chunk and sends it (plus some bookkeeping info) (6) The server puts the chunk into the every growing zip file, plus appropriate zip file crud. If the server orders the chunks it asks the client for appropriately, this can all be done by file appends.
服务器仅更新清单每个时间步6成功完成,所以在步骤3-6(包括服务器或客户端上崩溃)的故障可以被安全地(当然,更恢复或更少)。
在做块文件压缩文件创建时有一些小小的错误。需要实现的基本原则是找到一个可分块的压缩算法。 Deflate可以用这种方式使用。
java ZipOutputStream和DeflaterOutputStream并不真正适用于'明智的'放气/压缩,因为它们不允许任意刷新。在http://www.jcraft.com/jzlib有一个BSD风格的许可java实现ZLib。我没有对速度进行基准测试,但它提供了与Java实现相同的输出。 JZLib很棒,支持ZLib的所有刷新模式(与java.util.zip.Deflate实现不同)。
此外,Zip文件计算每个条目的CRC。因此,步骤4中的清单条目包含一个“部分”CRC,对每个块进行更新并在步骤5的簿记信息中将其发送回去。在http://www.axlradius.com/freestuff/CRC32.java处有一个针对java的CRC的公共域实现。我已经对它进行了基准测试,它和本地java实现一样快(并提供了等效的CRC)。
最后,Zip文件格式非常不合适。我设法将wikipedia页面和http://www.pkware.com/documents/casestudies/APPNOTE.TXT的大部分实现拼凑在一起。尽管在某一点上,我无法为其中一个领域提供正确的价值。幸运的是,JDK的ZipOutputStream源是可用的,所以你可以看到他们做了什么。
我不知道任何可以让你在流中恢复压缩的东西;这似乎是一个非常敏感的事情。相反,你可能会考虑将文件“分解”成更小的块并单独发送(通过压缩)。比如说,100kb的块(例如)。您仍然无法在大块中间恢复,但您可以轻松地从最近的块开始。
即时压缩很容易。你将遇到的问题是恢复上传。这基本上消除了HTTP作为一种传输方式,因此您需要查看诸如(S)FTP或SCP之类的内容。即使那里的问题是你没有在客户端上创建一个文件,所以会有什么恢复呢?至少你需要使用确定性的压缩方法(这意味着给定一个指定的文件,压缩算法的任何两次运行将产生完全相同的输出)。如果不是这样,则根本不能恢复,所有。
我的建议是采取稍微切线的方法。将文件分成可管理的块(比如50MB)。这是确定性的。分别压缩每个块。如果块失败,请重新发送。没有恢复,但您可以通过服务器部分上传告诉客户端接收或等待的内容。
你将遇到的一个问题是识别一个特定的文件。文件名会这样吗?有其他的识别特征吗?如果两个客户端尝试上传相同的文件,服务器是否能够检测到这一点?这种事情的标准方法是使用校验和(文件内容的SHA1散列),但不希望只读取16GB文件来完成校验和。所以其他一些方法会更好。
想象的网络通信是这样的:
Client: SEND file1234 CHUNKS 167
Server: RECEIVED (already got) or WAIT 7 (chunk #)
Client: compress and send chunk 7
Server: WAIT 8
....
这种方法也将处理多个客户端上传,同时为服务器上的文件可以要求从不同客户不同的块,并把它们合并起来。
这种方法的一个问题是文件在服务器上不是“完整的”(如zip或tarball),但我认为你需要放弃一些实际工作的东西,而不是成为代码的噩梦。
假设一个文件可以通过它的名字进行标识是安全的(它实际上比这更复杂一点,但是在系统中的其他地方处理ID的协商)。 (另外,请参阅对问题本身的评论,作为对分块思想的回复) – 2010-07-28 04:00:37
有几个人建议分块。除了上面的条件(2),这是一个好主意。如果我大块,我没有办法(我知道)从大块制作一个有效的.zip/.gz文件。 – 2010-07-28 03:56:44
就像我说的,你需要在客户端存储一个文件或者在服务器上重新组装。唯一的选择是有效地将整个文件加载到内存中,然后在客户端上创建文件的内存中版本。 – cletus 2010-07-28 04:03:11