1

我正在使用 ZipArchive + FileStream 创建一个 zip 文件。当新项目添加到 zip 文件中时,我想将新添加的项目刷新/写入到 zip 流下面。

下面的代码不会刷新单个 zip 项目。当 FileStream 处理时,整个 zip 被写入 output.zip。

        var files = Directory.GetFiles("C:\\Temp","*.pdf");
        using (var output = new FileStream("c:\\temp\\output.zip", FileMode.Create, FileAccess.Write))
        {
            using (System.IO.Compression.ZipArchive zip = new ZipArchive(output, ZipArchiveMode.Create, true))
            {                    
                foreach (var file in files)
                {
                    using (var internalFile = new FileStream(file, FileMode.Open))
                    {
                        
                        var zipItem = zip.CreateEntry(Path.GetFileName(file));
                                 
                        using var entryStream = zipItem.Open();
                        {
                            await internalFile.CopyToAsync(entryStream).ConfigureAwait(false);
                        }
                    }
                                            
                    await output.FlushAsync();

                    // after each file flush the output stream.
                    // expectation at this point, individual zip item will be written to physical file.
                    // however I don't see the file size changes in windows explorer.
                } // put breakpoint here
            }
        } // The whole output get flush at this point when FileStream is disposed            
4

1 回答 1

1

我要说“这是设计使然”。

看起来很难获得任何不同的行为。

从设计的角度来看,这可能有价值的原因与 zip 过程的工作方式有关。它识别重复的字节序列,而不是多次写入该序列,而是写入一次,然后每当需要该字节序列时,它写入一个引用,而不是整个序列。这就是 zip 文件变得比原始文件小的原因。(警告:这是我的理解,用通俗的话来说,我已经很长时间没有看到 zip 算法了)。

因此,在写入之前让整个文件可用,以优化重复字节序列的识别,这是“有价值的”。

这是来自 dotnet 运行时 github 存储库的一些看起来像 ZipArchive 的代码。

https://github.com/dotnet/runtime/blob/6072e4d3a7a2a1493f514cdf4be75a3d56580e84/src/libraries/System.IO.Compression/src/System/IO/Compression/ZipArchive.cs

(它可能不是最新的,也可能不是您正在运行的实际版本)。

看起来压缩是通过该private void WriteFile()方法完成的。当然,这就是seek(0)发生的地方。此方法是private并且仅从该Dispose()方法中引用。

您的代码正在调用FlushAsync()您的输出流。这是一个标准的 IO 文件流。当你调用FlushAsync()它时,它将写入ZipArchive对象给它的所有字节。不幸的是,这将是零字节。

您可以尝试在写入每个对象后处理 ZipArchive,但我认为这不是一个非常愉快的实验。我怀疑它每次都会重写整个流,而不是单独添加新元素(但我不确定)。

于 2020-11-24T22:21:26.717 回答