Обновите файл в ZipArchive - PullRequest
       26

Обновите файл в ZipArchive

0 голосов
/ 09 декабря 2018

У меня есть объект ZipArchive, который содержит XML-файл, который я изменяю.Затем я хочу вернуть измененный ZipArchive.

Вот код, который у меня есть:

var package = File.ReadAllBytes(/* location of existing .zip */);

using (var packageStream = new MemoryStream(package, true))
using (var zipPackage = new ZipArchive(packageStream, ZipArchiveMode.Update))
{
    // obtain the specific entry    
    var myEntry = zipPackage.Entries.FirstOrDefault(entry => /* code elided */));

    XElement xContents;
    using (var reader = new StreamReader(myEntry.Open()))
    {
        // read the contents of the myEntry XML file
        // then modify the contents into xContents
    }

    using (var writer = new StreamWriter(myEntry.Open()))
    {
        writer.Write(xContents.ToString());
    }

    return packageStream.ToArray();
}

Этот код вызывает исключение "Поток памяти не расширяемый" при вызове packageStream.ToArray().

Может кто-нибудь объяснить, что я сделал неправильно, и как правильно обновить существующий файл в ZipArchive?

1 Ответ

0 голосов
/ 09 декабря 2018

Очевидно, ZipArchive хочет расширить или изменить размер потока архива ZIP.Однако вы предоставили MemoryStream с фиксированной длиной потока (благодаря использованию конструктора MemoryStream(byte[], bool), который создает поток памяти с фиксированной длиной, равной длине массива, предоставленного дляконструктор).

Поскольку ZipArchive хочет расширить (или изменить размер) потока, предоставьте изменяемый размер MemoryStream (используя его конструктор без параметров).Затем скопируйте исходные данные файла в этот MemoryStream и продолжите манипуляции с ZIP-архивом.

И не забудьте сбросить MemoryStream позицию чтения / записи обратно в0 после копирования в него данных исходного файла, в противном случае ZipArchive будет видеть " End of Stream " только при попытке прочитать данные архива ZIP из этого потока.

using (var packageStream = new MemoryStream())
{
    using (var fs = File.OpenRead(/* location of existing .zip */))
    {
        fs.CopyTo(packageStream);
    }

    packageStream.Position = 0;


    using (var zipPackage = new ZipArchive(packageStream, ZipArchiveMode.Update))
    {
        ... do your thing ...
    }


    return packageStream.ToArray();
}

Этот код содержит еще одно исправление.В исходном коде, о котором идет речь, return packageStream.ToArray(); был помещен в блок using ZipArchive .Во время выполнения этой строки экземпляр ZipArchive , возможно, еще не записал все данные в MemoryStream , возможно, некоторые данные остались в некоторых внутренних буферах и / или, возможно, были отложенызапись некоторых структур данных ZIP.

Чтобы ZipArchive действительно полностью записал все необходимые данные в MemoryStream , здесь достаточно переместить return packageStream.ToArray(); за пределыпосле блока ZipArchive using.В конце блока using будет удален ZipArchive , что также обеспечит запись ZipArchive всех пока не записанных данных в поток.Таким образом, доступ к MemoryStream после удаления ZipArchive приведет к получению полных данных полностью обновленного ZIP-архива.


Примечание: делайте это только с небольшими файлами ZIP. MemoryStream , очевидно, будет использовать внутренние буферы данных (массивы) для хранения данных в MemoryStream .Однако packageStream.ToArray(); создаст копию данных в MemoryStream , поэтому в течение некоторого времени требования к памяти для этой подпрограммы будут чуть более чем вдвое превышать размер ZIP-архива.

...