ZipArchive создает недопустимый ZIP файл

Я пытаюсь создать новый ZIP-пакет из кода с одной записью и сохранить ZIP файл в файл. Я пытаюсь добиться этого с помощью класса System.IO.Compression.ZipArchive. Я создаю пакет ZIP со следующим кодом:

using (MemoryStream zipStream = new MemoryStream())
{
    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create))
    {
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine(
                "Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }

Затем я сохраняю ZIP в файл либо в WinRT:

        var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("test.zip", CreationCollisionOption.ReplaceExisting);
        zipStream.Position = 0;
        using (Stream s = await file.OpenStreamForWriteAsync())
        {
            zipStream.CopyTo(s);
        }

Или в обычном .NET 4.5:

        using (FileStream fs = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
        {
            zipStream.Position = 0;
            zipStream.CopyTo(fs);
        }

Однако я не могу открыть созданные файлы ни в проводнике Windows, ни в WinRAR и т.д. (я проверил, что размер созданного файла соответствует длине zipStream, поэтому сам поток был сохранен в файле правильно. )
Я что-то делаю неправильно или есть проблема с классом ZipArchive?

Ответ 1

Я нашел ретроспективную, очевидную ошибку в моем коде. ZipArchive должен быть расположен, чтобы заставить его записывать свой контент в его базовый поток. Поэтому мне пришлось сохранить поток в файл после окончания блока использования ZipArchive.
И было важно установить аргумент leaveOpen его конструктора в true, чтобы он не закрывал базовый поток. Итак, вот полное рабочее решение:

using (MemoryStream zipStream = new MemoryStream())
{
    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
    {
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine(
                "Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }
    }

    var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(
        "test.zip",
        CreationCollisionOption.ReplaceExisting);

    zipStream.Position = 0;
    using (Stream s = await file.OpenStreamForWriteAsync())
    {
        zipStream.CopyTo(s);
    }
}

Ответ 2

Во всем своем потоковом объекте вы должны перематывать потоки с самого начала, чтобы они были правильно прочитаны другими приложениями с использованием метода .Seek.

Пример:

zipStream.Seek(0, SeekOrigin.Begin);

Ответ 3

Вы можете следовать той же идее, только в обратном порядке, используя поток файлов, такой как источник. Я сделал форму ниже, и файл был открыт нормально:

string fileFormat = ".zip"; // any format
string filename = "teste" + fileformat;

StorageFile zipFile = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync(filename,CreationCollisionOption.ReplaceExisting);

using (Stream zipStream = await zipFile.OpenStreamForWriteAsync()){

   using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create)){
        //Include your content here
   }
}

Ответ 4

// Create file "archive.zip" in current directory use it as destination for ZIP archive
using (var zipArchive = new ZipArchive(File.OpenWrite("archive.zip"), 
ZipArchiveMode.Create))
{
    // Create entry inside ZIP archive with name "test.txt"
    using (var entry = zipArchive.CreateEntry("test.txt").Open())
    {
        // Copy content from current directory file "test.txt" into created ZIP entry 
        using (var file = File.OpenRead("test.txt"))
        {
            file.CopyTo(entry);
        }
    }
}    

В результате вы получите архив "archive.zip" с одним файлом "test.txt". Вам нужен этот каскад using, чтобы избежать взаимодействия с уже расположенными ресурсами.

Ответ 5

Полный код выглядит следующим образом:

var file = await Windows.Storage.ApplicationData.Current.LocalFolder.CreateFileAsync("test.zip",CreationCollisionOption.ReplaceExisting);
using (Stream zipStream = await zipFile.OpenStreamForWriteAsync())
{
    using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
    {
        var entry = zip.CreateEntry("test.txt");
        using (StreamWriter sw = new StreamWriter(entry.Open()))
        {
            sw.WriteLine("Etiam eros nunc, hendrerit nec malesuada vitae, pretium at ligula.");
        }
    }   
}

Ответ 6

В моем случае проблема заключалась в том, что я не использовал "zipArchive" при завершении задачи zip файла. Даже если я смывал и закрывал все потоки.

Итак, либо используйте, как предложено в ответах выше

using (var zipArchive = new ZipArchive(File.OpenWrite("archive.zip"), 
ZipArchiveMode.Create))

Или избавиться от переменной в конце задачи...

zipArchive.Dispose();