web-dev-qa-db-ja.com

ZipArchiveは無効なZipファイルを作成します

1つのエントリを持つコードから新しい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.");
        }

次に、WinRTでZipをファイルに保存します。

        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クラスに問題がありますか?

44
Mark Vincze

レトロスペクティブで明らかなエラーがコードに見つかりました。 ZipArchiveは、コンテンツを基本ストリームに書き込むためにdisposedにする必要があります。そのため、ZipArchiveのusingブロックの終了後に、ストリームをファイルに保存する必要がありました。
そして、コンストラクターの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);
    }
}
85
Mark Vincze

すべてのストリームオブジェクトで、.Seekメソッドを使用して他のアプリケーションによって正しく読み取られるように、ストリームを最初から巻き戻す必要があります。

例:

zipStream.Seek(0, SeekOrigin.Begin);
2
Freeman

Sourceのようなファイルストリームを使用して、逆の順序でのみ同じ考え方に従うことができます。以下のフォームを実行し、ファイルが正常に開かれました:

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
   }
}
1
Robert Melo
// 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);
        }
    }
}    

その結果、単一のエントリファイル「test.txt」を含むアーカイブ「archive.Zip」が取得されます。このカスケードusingは、既に破棄されているリソースとの相互作用を避けるために必要です。

1
Andrew Dragan

完全なコードは次のようになります。

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.");
        }
    }   
}
0
Robert Melo

私の場合、問題は、Zipファイルのタスクが完了したときに"zipArchive"を破棄しなかったことです。すべてのストリームをフラッシュして閉じていたにもかかわらず。

したがって、上記の回答で示唆されているように、usingを使用します

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

または、タスクの最後に変数を破棄します...

zipArchive.Dispose();
0
Ritesh Mishra