C#でメモリ内の複数のファイルからZipアーカイブを作成する
-
07-07-2019 - |
質問
ファイルが現在メモリにあるときに、複数のファイルを含むZipアーカイブを作成する方法はありますか?保存したいファイルは実際にはテキストのみであり、アプリケーションの文字列クラスに保存されます。しかし、単一の自己完結型アーカイブに複数のファイルを保存したいと思います。それらはすべてアーカイブのルートに置くことができます。
SharpZipLibを使用してこれを実行できると便利です。
解決
これには ZipEntry
と PutNextEntry()
を使用します。以下はファイルに対してそれを行う方法を示していますが、メモリ内のオブジェクトに対してはMemoryStreamを使用します
FileStream fZip = File.Create(compressedOutputFile);
ZipOutputStream zipOStream = new ZipOutputStream(fZip);
foreach (FileInfo fi in allfiles)
{
ZipEntry entry = new ZipEntry((fi.Name));
zipOStream.PutNextEntry(entry);
FileStream fs = File.OpenRead(fi.FullName);
try
{
byte[] transferBuffer[1024];
do
{
bytesRead = fs.Read(transferBuffer, 0, transferBuffer.Length);
zipOStream.Write(transferBuffer, 0, bytesRead);
}
while (bytesRead > 0);
}
finally
{
fs.Close();
}
}
zipOStream.Finish();
zipOStream.Close();
他のヒント
これにSharpZipLibを使用するのはかなり複雑に思えます。これは、 DotNetZip の方がはるかに簡単です。 v1.9では、コードは次のようになります。
using (ZipFile zip = new ZipFile())
{
zip.AddEntry("Readme.txt", stringContent1);
zip.AddEntry("readings/Data.csv", stringContent2);
zip.AddEntry("readings/Index.xml", stringContent3);
zip.Save("Archive1.zip");
}
上記のコードは、stringContent {1,2,3}にzipアーカイブ内のファイル(またはエントリ)に保存されるデータが含まれていることを前提としています。最初のエントリは" Readme.txt"です。最上位の「ディレクトリ」に保存されます。 zipアーカイブ内。次の2つのエントリは「読み値」に保存されます。 zipアーカイブのディレクトリ。
文字列はデフォルトのエンコードでエンコードされます。ここには示されていないAddEntry()のオーバーロードがあり、使用するエンコーディングを明示的に指定できます。
文字列ではなくストリームまたはバイト配列にコンテンツがある場合、それらの型を受け入れるAddEntry()のオーバーロードがあります。また、データをzipに書き込むために呼び出されるWriteデリゲートを受け入れるオーバーロードもあります。これは、たとえばDataSetをzipファイルに簡単に保存する場合に機能します。
DotNetZipは無料でオープンソースです。
はい、SharpZipLibを使用してこれを行うことができます-書き込むストリームを提供する必要がある場合は、 MemoryStream
を使用します。
このクラスを作成したMSDNの例を使用して、この問題に遭遇しました:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Packaging;
using System.IO;
public class ZipSticle
{
Package package;
public ZipSticle(Stream s)
{
package = ZipPackage.Open(s, FileMode.Create);
}
public void Add(Stream stream, string Name)
{
Uri partUriDocument = PackUriHelper.CreatePartUri(new Uri(Name, UriKind.Relative));
PackagePart packagePartDocument = package.CreatePart(partUriDocument, "");
CopyStream(stream, packagePartDocument.GetStream());
stream.Close();
}
private static void CopyStream(Stream source, Stream target)
{
const int bufSize = 0x1000;
byte[] buf = new byte[bufSize];
int bytesRead = 0;
while ((bytesRead = source.Read(buf, 0, bufSize)) > 0)
target.Write(buf, 0, bytesRead);
}
public void Close()
{
package.Close();
}
}
次のように使用できます:
FileStream str = File.Open("MyAwesomeZip.zip", FileMode.Create);
ZipSticle zip = new ZipSticle(str);
zip.Add(File.OpenRead("C:/Users/C0BRA/SimpleFile.txt"), "Some directory/SimpleFile.txt");
zip.Add(File.OpenRead("C:/Users/C0BRA/Hurp.derp"), "hurp.Derp");
zip.Close();
str.Close();
MemoryStream
(または任意の Stream
)を次のような ZipSticle.Add
に渡すことができます。
FileStream str = File.Open("MyAwesomeZip.zip", FileMode.Create);
ZipSticle zip = new ZipSticle(str);
byte[] fileinmem = new byte[1000];
// Do stuff to FileInMemory
MemoryStream memstr = new MemoryStream(fileinmem);
zip.Add(memstr, "Some directory/SimpleFile.txt");
memstr.Close();
zip.Close();
str.Close();
この回答は時代遅れです。 .Net 4.5以降、 ZipArchive
クラスにより、ファイルをメモリ内に圧縮できます。使用方法については、下記のjohnny 5の回答をご覧ください。
すべての文字列を格納するためにSerializableオブジェクトを使用して、少し異なる方法で行うこともできます
[Serializable]
public class MyStrings {
public string Foo { get; set; }
public string Bar { get; set; }
}
次に、ストリームにシリアル化して保存できます。
スペースを節約するために、GZipStream(From System.IO.Compression)を使用して圧縮できます。 (注:GZipはストリーム圧縮であり、複数のファイルのアーカイブではありません。)
もちろん、必要なのは実際にデータを保存することであり、他のソフトウェア用に特定の形式でいくつかのファイルを圧縮することではない場合です。 また、これにより、文字列を除くより多くのタイプのデータを保存できます。
この関数は、データのストリームからバイト配列を作成する必要があります。簡単にするためにファイルを処理するためのシンプルなインターフェイスを作成しました
public interface IHasDocumentProperties
{
byte[] Content { get; set; }
string Name { get; set; }
}
public void CreateZipFileContent(string filePath, IEnumerable<IHasDocumentProperties> fileInfos)
{
using (var memoryStream = new MemoryStream())
using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
foreach(var fileInfo in fileInfos)
{
var entry = zipArchive.CreateEntry(fileInfo.Name);
using (var entryStream = entry.Open())
{
entryStream.Write(fileInfo.Content, 0, fileInfo.Content.Length);
}
}
}
using (var fileStream = new FileStream(filePath, FileMode.OpenOrCreate, System.IO.FileAccess.Write))
{
memoryStream.CopyTo(fileStream);
}
}
MemoryStream
を異なるExcelのソースとして追加することで Cheesoの答えを利用していましたファイル。 zipをダウンロードしたとき、ファイルには何も含まれていませんでした。これは、AJAXを介してファイルを作成してダウンロードしようとする方法です。
異なるExcelファイルの内容をZipに含めるには、各ファイルを byte []
として追加する必要がありました。
using (var memoryStream = new MemoryStream())
using (var zip = new ZipFile())
{
zip.AddEntry("Excel File 1.xlsx", excelFileStream1.ToArray());
zip.AddEntry("Excel File 2.xlsx", excelFileStream2.ToArray());
// Keep the file off of disk, and in memory.
zip.Save(memoryStream);
}
StringReader を使用して、文字列オブジェクトをStreamとして公開します。
これにより、zipビルドコードに簡単にフィードできるようになります。