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クラスに問題がありますか?
レトロスペクティブで明らかなエラーがコードに見つかりました。 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);
}
}
すべてのストリームオブジェクトで、.Seekメソッドを使用して他のアプリケーションによって正しく読み取られるように、ストリームを最初から巻き戻す必要があります。
例:
zipStream.Seek(0, SeekOrigin.Begin);
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
}
}
// 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
は、既に破棄されているリソースとの相互作用を避けるために必要です。
完全なコードは次のようになります。
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.");
}
}
}
私の場合、問題は、Zipファイルのタスクが完了したときに"zipArchive"を破棄しなかったことです。すべてのストリームをフラッシュして閉じていたにもかかわらず。
したがって、上記の回答で示唆されているように、usingを使用します
using (var zipArchive = new ZipArchive(File.OpenWrite("archive.Zip"),
ZipArchiveMode.Create))
または、タスクの最後に変数を破棄します...
zipArchive.Dispose();