ZipArchive не сразу сбрасывает zip-архив

Я создаю zip-файл, используя ZipArchive + FileStream. Когда новый элемент добавляется в zip-файл, я хотел бы сбросить/записать вновь добавленный элемент в нижний zip-поток.

Приведенный ниже код не очищает отдельный элемент почтового индекса. Весь zip записывается в output.zip при удалении FileStream.

        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            

person LP13    schedule 24.11.2020    source источник
comment
Я предполагаю, что потоки будут сброшены, когда их буферы будут заполнены. Достаточно ли велики файлы, чтобы вызвать это?   -  person Stephen Cleary    schedule 25.11.2020


Ответы (1)


Я собираюсь сказать, что это по дизайну.

Конечно, похоже, что будет трудно получить какое-либо другое поведение.

Причина, по которой это может быть полезно с точки зрения дизайна, связана с тем, как работает процесс zip. Он идентифицирует повторяющиеся серии байтов, и вместо того, чтобы записывать эту серию несколько раз, он записывает ее один раз, а затем всякий раз, когда требуется эта последовательность байтов, он записывает ссылку, а не всю последовательность. Вот почему zip-файл становится меньше исходного файла. (Предупреждение: это мое понимание, с точки зрения непрофессионала, и прошло много времени с тех пор, как я смотрел на алгоритм zip).

Таким образом, «полезно» иметь весь файл доступным до его записи, чтобы оптимизировать идентификацию повторяющихся последовательностей байтов.

Это код, похожий на ZipArchive, из репозитория github среды выполнения dotnet.

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() в вашем потоке вывода. Это стандартный поток файлов ввода-вывода. Когда вы вызываете FlushAsync(), он будет записывать все байты, которые дал ему объект ZipArchive. К сожалению, это будут нулевые байты.

Вы можете попробовать удалять ZipArchive после записи каждого объекта, но я думаю, что это будет не очень удачный эксперимент. Я подозреваю, что он будет каждый раз переписывать весь поток, а не добавлять новые элементы по отдельности (но я не уверен).

person GregHNZ    schedule 24.11.2020