首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么这个ZipArchive甲骨文要关闭5-10字节?

为什么这个ZipArchive甲骨文要关闭5-10字节?
EN

Stack Overflow用户
提问于 2017-12-14 17:11:24
回答 1查看 114关注 0票数 0

我正在使用ZipArchive,我正在编写一个oracle,它根据拉链规格确定压缩文件的大小。为了简单起见,不使用压缩。

代码语言:javascript
复制
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个字节,并且是一个空压缩的适当大小。

代码语言:javascript
复制
[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个字节

代码语言:javascript
复制
[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个字节

代码语言:javascript
复制
[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个字节

代码语言:javascript
复制
[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);
    }
}

为了让甲骨文给出正确的文件大小,我在规范中遗漏了什么?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-12-18 20:01:53

您缺少下列内容:

  1. 数据描述符块是可选的,只有在压缩文件以“流”方式编写时才会包含(也就是说,您事先不知道文件的大小并“动态”写入)。当您是流大小的压缩和未压缩的数据,以及CRC,当写入文件头时(因为文件头位于数据之前),所以当这些信息可用时,文件头中的所有字节都被设置为0,并且在压缩数据之后包含数据描述符块。如果提供了示例,则不包括数据描述符。
  2. NoCompression级别在CreateEntry中并不意味着数据按字面意思包括在内。相反,数据是用压缩算法处理的(您链接的规范中的压缩方法8),而没有实际的压缩。这种压缩算法增加了自己的开销,即使在“无压缩模式”下也是如此:
代码语言:javascript
复制
- 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个字节。

  1. 您使用writer.WriteLine,因此在第一个示例中写入的数据大小不是4个字节,而是6个字节,因为添加了\r\n (换行符)(在第二个示例中为13)。

如果考虑到所有这些(删除12个数据描述符大小,为您的小文件添加5个大小的泄气开销,传递正确的totalSizeOfFiles) --您的示例将产生预期的输出。

更新有关数据描述符记录。说明书上说:

只有当无法在输出.ZIP文件中查找时,例如,当输出.ZIP文件是标准输出或不可查找的设备时,才应使用此描述符。

ZipArchive类如下所示。如果在构造函数中传递不可查找的流,它将发出数据描述符记录。例如:

代码语言:javascript
复制
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。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47818771

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档