web-dev-qa-db-ja.com

C#およびSystem.IO.Packagingを使用してプログラムでZipアーカイブからファイルを抽出する

私はいくつかの階層的な再編成と抽出を切実に必要としているZipファイルをたくさん持っています。現在できることは、ディレクトリ構造を作成し、Zipファイルを適切な場所に移動することです。欠けている神秘的なチーズは、Zipアーカイブからファイルを抽出する部分です。

私はZipArchiveクラスに関するMSDNの記事を見て、それらを十分に理解しています。 VBScriptの抽出方法 も見ました。これは複雑なクラスではないので、ものの抽出は非常に簡単なはずです。実際、それは「ほぼ」機能します。参考のために、現在のコードを以下に記載しています。

 using (ZipPackage package = (ZipPackage)Package.Open(@"..\..\test.Zip", FileMode.Open, FileAccess.Read))
 {
    PackagePartCollection packageParts = package.GetParts();
    foreach (PackageRelationship relation in packageParts)
    {
       //Do Stuff but never gets here since packageParts is empty.
    }
 }

問題はGetParts(またはそれについてはGetAnything)のどこかにあるようです。パッケージは、開いている間は空のようです。デバッガを深く掘り下げると、プライベートメンバー_zipArchiveが実際にパーツを持っていることがわかります。適切な名前とすべての部品。 GetParts関数がそれらを取得しないのはなぜですか?私はオープンをZipArchiveにキャストしようとしましたが、それは役に立ちませんでした。 Grrr。

47
Craig

Zipファイルを操作する場合は、サードパーティのライブラリを調べてください。

たとえば、最近更新されたDotNetZip。現在のバージョンはv1.8です。 Zipを作成する例を次に示します。

using (ZipFile Zip = new ZipFile())
{
  Zip.AddFile("c:\\photos\\personal\\7440-N49th.png");
  Zip.AddFile("c:\\Desktop\\2005_Annual_Report.pdf");
  Zip.AddFile("ReadMe.txt");

  Zip.Save("Archive.Zip");
}

以下は、既存のZipをupdateする例です。あなたはそれを行うためにファイルを抽出する必要はありません:

using (ZipFile Zip = ZipFile.Read("ExistingArchive.Zip"))
{
  // 1. remove an entry, given the name
  Zip.RemoveEntry("README.txt");

  // 2. Update an existing entry, with content from the filesystem
  Zip.UpdateItem("Portfolio.doc");

  // 3. modify the filename of an existing entry 
  // (rename it and move it to a sub directory)
  ZipEntry e = Zip["Table1.jpg"];
  e.FileName = "images/Figure1.jpg";

  // 4. insert or modify the comment on the Zip archive
  Zip.Comment = "This Zip archive was updated " + System.DateTime.ToString("G"); 

  // 5. finally, save the modified archive
  Zip.Save();
}

エントリを抽出する例を次に示します。

using (ZipFile Zip = ZipFile.Read("ExistingZipFile.Zip"))
{
  foreach (ZipEntry e in Zip)
  {
    e.Extract(TargetDirectory, true);  // true => overwrite existing files
  }
}

DotNetZipは、ファイル名のマルチバイト文字、Zip暗号化、AES暗号化、ストリーム、Unicode、自己解凍アーカイブをサポートしています。また、ファイル長が0xFFFFFFFFを超える場合、または65535エントリを超えるアーカイブの場合、Zip64も同様です。

自由。オープンソース

codeplex または windows.netからの直接ダウンロード で入手-CodePlexは廃止され、アーカイブされました

47
Cheeso

[〜#〜] msdn [〜#〜] から、

このサンプルでは、​​Packageクラスが使用されています(ZipPackageとは対照的です)。両方で作業を行った結果、Zipファイルに破損がある場合にのみフラキネスが発生することがわかりました。 WindowsエクストラクタまたはWinzipをスローする破損とは限りませんが、パッケージングコンポーネントの処理に問題があります。

これがお役に立てば幸いです。問題のデバッグに代わる方法を提供できるかもしれません。

using System;
using System.IO;
using System.IO.Packaging;
using System.Text;

class ExtractPackagedImages
{
    static void Main(string[] paths)
    {
        foreach (string path in paths)
        {
            using (Package package = Package.Open(
                path, FileMode.Open, FileAccess.Read))
            {
                DirectoryInfo dir = Directory.CreateDirectory(path + " Images");
                foreach (PackagePart part in package.GetParts())
                {
                    if (part.ContentType.ToLowerInvariant().StartsWith("image/"))
                    {
                        string target = Path.Combine(
                            dir.FullName, CreateFilenameFromUri(part.Uri));
                        using (Stream source = part.GetStream(
                            FileMode.Open, FileAccess.Read))
                        using (Stream destination = File.OpenWrite(target))
                        {
                            byte[] buffer = new byte[0x1000];
                            int read;
                            while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
                            {
                                destination.Write(buffer, 0, read);
                            }
                        }
                        Console.WriteLine("Extracted {0}", target);
                    }
                }
            }
        }
        Console.WriteLine("Done");
    }

    private static string CreateFilenameFromUri(Uri uri)
    {
        char [] invalidChars = Path.GetInvalidFileNameChars();
        StringBuilder sb = new StringBuilder(uri.OriginalString.Length);
        foreach (char c in uri.OriginalString)
        {
            sb.Append(Array.IndexOf(invalidChars, c) < 0 ? c : '_');
        }
        return sb.ToString();
    }
}
44
jro

ZipPackage Class (MSDN)」から:

パッケージはZipPackageクラスを介してZipファイル*として保存されますが、すべてのZipファイルはZipPackagesではありません。 ZipPackageには、URI準拠のファイル(パーツ)名や、パッケージに含まれるすべてのファイルのMIMEタイプを定義する「[Content_Types] .xml」ファイルなどの特別な要件があります。 ZipPackageクラスを使用して、Open Packaging Conventions標準に準拠していない任意のZipファイルを開くことはできません。

詳細については、ECMA International「Open Packaging Conventions」標準のセクション9.2「Zipアーカイブへのマッピング」を参照してください http://www.ecma-international.org/publications/files/ECMA-ST/Office% 20Open%20XML%20Part%202%20(DOCX).Zip (342Kb)または http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML %20Part%202%20(PDF).Zip (1.3Mb)

* ZipPackageベースのファイル(.docx、.xlsx、.pptxなど)の拡張子に「.Zip」を追加するだけで、お気に入りのZipユーティリティで開くことができます。

31
Luke

私はまったく同じ問題を抱えていました! GetParts()メソッドで何かを返すには、[Content_Types] .xmlファイルをアーカイブのルートに追加し、含まれるすべてのファイル拡張子の「デフォルト」ノードを追加する必要がありました。 (Windowsエクスプローラーを使用して)これを追加すると、コードはアーカイブされたコンテンツを読み取って抽出することができました。

[Content_Types] .xmlファイルの詳細については、次を参照してください。

http://msdn.Microsoft.com/en-us/magazine/cc163372.aspx -記事の図13の下にサンプルファイルがあります。

var zipFilePath = "c:\\myfile.Zip"; 
var tempFolderPath = "c:\\unzipped"; 

using (Package package = ZipPackage.Open(zipFilePath, FileMode.Open, FileAccess.Read)) 
{ 
    foreach (PackagePart part in package.GetParts()) 
    { 
        var target = Path.GetFullPath(Path.Combine(tempFolderPath, part.Uri.OriginalString.TrimStart('/'))); 
        var targetDir = target.Remove(target.LastIndexOf('\\')); 

        if (!Directory.Exists(targetDir)) 
            Directory.CreateDirectory(targetDir); 

        using (Stream source = part.GetStream(FileMode.Open, FileAccess.Read)) 
        { 
            FileStream targetFile = File.OpenWrite(target);
            source.CopyTo(targetFile);
            targetFile.Close();
        } 
    } 
} 

注:このコードは.NET 4.0のStream.CopyToメソッドを使用します

13
Joshua

私はチーソーに同意します。 System.IO.Packagingは、一般的なZipファイルを処理する場合、Office Open XMLドキュメント用に設計されているため、扱いにくいです。 DotNetZip または SharpZipLib を使用することをお勧めします

6
Rad

(これは基本的に this answer の言い換えです)

System.IO.Packaging.ZipPackageはPKZIPをサポートしていません。そのため、「汎用」Zipファイルを開いたときに「パーツ」は返されません。このクラスは、SDK 1.6までのWindows Azureサービスパッケージとして使用されるZipファイルの特定のフレーバー( MSDNの説明 の下部にあるコメントを参照)のみをサポートします。そのため、サービスパッケージをアンパックしてからInfo-Zipパッカーを使用して再パックすると、無効になります。

1
sharptooth