我正在使用ZipArchive,我正在编写一个oracle,它根据拉链规格确定压缩文件的大小。为了简单起见,不使用压缩。
private long ZipSizeOracle(int numOfFiles, int totalLengthOfFilenames, int totalSizeOfFiles)
{
return
numOfFiles * (
30 //Local file header
+
12 //Data descriptor
+
46 //Central directory file header
)
+
2 * totalLengthOfFilenames //Local file header name + Central directory file header name
+
totalSizeOfFiles //Data size
+ 22 //End of central directory record (EOCD)
;
}目前,我有4个测试,ZeroFiles正确地输出了22个字节,并且是一个空压缩的适当大小。
[TestMethod]
public void ZeroFiles()
{
using (var memStream = new MemoryStream())
{
using (var archive = new ZipArchive(memStream, ZipArchiveMode.Create, true)) { }
Assert.AreEqual(ZipSizeOracle(0, 0, 0), memStream.Length);
}
}One4ByteFile期望130个字节,但实际为125个字节
[TestMethod]
public void One4ByteFile()
{
using (var memStream = new MemoryStream())
{
using (var archive = new ZipArchive(memStream, ZipArchiveMode.Create, true))
{
var entry1 = archive.CreateEntry("test.txt", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(entry1.Open()))
writer.WriteLine("test");
}
Assert.AreEqual(ZipSizeOracle(1, 8, 4), memStream.Length);
}
}Two4ByteFiles期望241个字节,但实际是231个字节
[TestMethod]
public void Two4ByteFiles()
{
using (var memStream = new MemoryStream())
{
using (var archive = new ZipArchive(memStream, ZipArchiveMode.Create, true))
{
var entry1 = archive.CreateEntry("test.txt", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(entry1.Open()))
writer.WriteLine("test");
var entry2 = archive.CreateEntry("test2.txt", CompressionLevel.NoCompression);
using (var writer = new StreamWriter(entry2.Open()))
writer.WriteLine("test2");
}
Assert.AreEqual(ZipSizeOracle(2, 17, 9), memStream.Length);
}
}OneFolder期望118个字节,但实际是108个字节
[TestMethod]
public void OneFolder()
{
using (var memStream = new MemoryStream())
{
using (var archive = new ZipArchive(memStream, ZipArchiveMode.Create, true))
archive.CreateEntry(@"test\", CompressionLevel.NoCompression);
Assert.AreEqual(ZipSizeOracle(1, 4, 0), memStream.Length);
}
}为了让甲骨文给出正确的文件大小,我在规范中遗漏了什么?
发布于 2017-12-18 20:01:53
您缺少下列内容:
NoCompression级别在CreateEntry中并不意味着数据按字面意思包括在内。相反,数据是用压缩算法处理的(您链接的规范中的压缩方法8),而没有实际的压缩。这种压缩算法增加了自己的开销,即使在“无压缩模式”下也是如此:- 1 byte defines if this is a last block or not and compression level.
- 2 bytes define block size
- 2 bytes define two-complement of block size (for integrity)
- then goes the data with size defined above
因此,对于输入中的每个数据块(块为2^16字节)-增加了5字节的开销。在您的示例中,所有文件的大小都小于2^16,因此只为它们添加了5个字节。
writer.WriteLine,因此在第一个示例中写入的数据大小不是4个字节,而是6个字节,因为添加了\r\n (换行符)(在第二个示例中为13)。如果考虑到所有这些(删除12个数据描述符大小,为您的小文件添加5个大小的泄气开销,传递正确的totalSizeOfFiles) --您的示例将产生预期的输出。
更新有关数据描述符记录。说明书上说:
只有当无法在输出.ZIP文件中查找时,例如,当输出.ZIP文件是标准输出或不可查找的设备时,才应使用此描述符。
ZipArchive类如下所示。如果在构造函数中传递不可查找的流,它将发出数据描述符记录。例如:
public class UnseekableStream : MemoryStream {
public override bool CanSeek => false;
}
using (var memStream = new UnseekableStream()) {
using (var archive = new ZipArchive(memStream, ZipArchiveMode.Create, true)) {
}
}这种不可见的流经常发生在实践中,http响应流就是一个例子。但请注意,12个字节并不是数据描述符记录的唯一允许大小:
4.3.9.3虽然最初没有分配签名,但值0x08074b50通常被用作数据描述符记录的签名值。实现者应该意识到,无论是否有此签名标记数据描述符,都可能遇到ZIP文件,并且在读取ZIP文件时应考虑到这两种情况,以确保兼容性。4.3.9.4在编写ZIP文件时,实现者应该包括标记数据描述符记录的签名值。当使用签名时,当前为数据描述符记录定义的字段将立即跟随签名。
因此,数据描述符可以选择以4个字节签名开始,并且建议实现者在写入时包含该签名,ZipArchive遵循此建议,因此数据描述符记录的大小为16字节(签名的12 +4),而不是12。
https://stackoverflow.com/questions/47818771
复制相似问题