web-dev-qa-db-ja.com

C#でメモリ内の複数のファイルからZipアーカイブを作成する

ファイルが現在メモリ内にあるときに、複数のファイルを含むZipアーカイブを作成する方法はありますか?保存したいファイルは実際にはテキストのみで、アプリケーションの文字列クラスに保存されています。しかし、私は複数のファイルを単一の自己完結型アーカイブに保存したいと思います。それらはすべてアーカイブのルートに置くことができます。

SharpZipLibを使用してこれを行うことができればいいのですが。

29
Adam Haile

これにはZipEntryPutNextEntry()を使用します。以下は、ファイルに対してそれを行う方法を示していますが、メモリ内オブジェクトに対しては、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();
25
Cory

これに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アーカイブの「readings」ディレクトリに保存されます。

文字列はデフォルトのエンコーディングでエンコードされます。ここには示されていないAddEntry()のオーバーロードがあり、使用するエンコーディングを明示的に指定できます。

コンテンツが文字列ではなくストリームまたはバイト配列にある場合、それらのタイプを受け入れるAddEntry()のオーバーロードがあります。 Zipにデータを書き込むために呼び出される独自のメソッドである書き込みデリゲートを受け入れるオーバーロードもあります。これは、たとえばDataSetをZipファイルに簡単に保存するために機能します。

DotNetZipは無料でオープンソースです。

20
Cheeso

はい。SharpZipLibを使用してこれを行うことができます。書き込むストリームを提供する必要がある場合は、MemoryStreamを使用します。

5
Jon Skeet

この問題に遭遇し、このクラスを作成した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();
4
Ashleigh

この関数は、データのストリームからバイト配列を作成する必要があります。単純化するためにファイルを処理するための単純なインターフェースを作成しました

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);
    }
}
2
johnny 5

私はさまざまなExcelファイルのソースとしてMemoryStreamsを追加することで 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);
}
2
krillgar

この回答は古くなっています。 .Net 4.5以降、ZipArchiveクラスを使用すると、メモリ内でファイルを圧縮できます。使い方は 下のジョニー5の回答 を参照してください。


Serializableオブジェクトを使用してすべての文字列を格納することもできます。

[Serializable]
public class MyStrings {
    public string Foo { get; set; }
    public string Bar { get; set; }
}

次に、それをストリームにシリアル化して保存します。
スペースを節約するために、GZipStream(From System.IO.Compression)を使用して圧縮できます。 (注:GZipはストリーム圧縮であり、複数のファイルのアーカイブではありません)。

つまり、もちろん必要なのは実際にデータを保存することであり、他のソフトウェア用に特定の形式でいくつかのファイルを圧縮することではありません。また、これにより、文字列以外のより多くのタイプのデータを保存できます。

2
configurator