Извлечение файлов из архива Zip программным способом с использованием C # и System.IO.Packaging - PullRequest
47 голосов
/ 03 февраля 2009

У меня есть куча ZIP-файлов, которые отчаянно нуждаются в некоторой иерархической реорганизации и извлечении. В настоящее время я могу создать структуру каталогов и переместить zip-файлы в нужное место. Мистический сыр, который мне не хватает, это часть, которая извлекает файлы из ZIP-архива.

Я видел статьи MSDN по классу ZipArchive и хорошо их понимаю. Я также видел 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 (или Get Anything в этом отношении). Кажется, что пакет, пока открыт, пуст. Копая глубже, отладчик показывает, что закрытый член _zipArchive показывает, что у него действительно есть части. Запчасти с правильными именами и все. Почему функция GetParts не может их получить? Я всегда пытался открыть ZipArchive, но это не помогло. Хмм.

Ответы [ 6 ]

47 голосов
/ 10 февраля 2009

Если вы манипулируете файлами ZIP, вы можете обратиться к сторонней библиотеке, чтобы помочь вам.

Например, DotNetZip, который был недавно обновлен. Текущая версия сейчас v1.8. Вот пример для создания почтового индекса:

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");
}

Вот пример обновления существующего почтового индекса; Вам не нужно извлекать файлы, чтобы сделать это:

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, самораспаковывающиеся архивы. Также подходит ZIP64 для файлов длиной более 0xFFFFFFFF или для архивов с более чем 65535 записями.

бесплатно. с открытым исходным кодом

получить его на codeplex или прямая загрузка с windows.net - CodePlex прекращен и заархивирован

44 голосов
/ 03 февраля 2009

С MSDN ,

В этом примере используется класс Package (в отличие от ZipPackage). Работая с обоими, я видел, как бесполезно происходить, когда в zip-файле есть повреждение. Не обязательно повреждение, которое выбрасывает экстрактор Windows или Winzip, но что-то, что компоненты Package имеют проблемы при обработке.

Надеюсь, это поможет, может быть, это может дать вам альтернативу отладке проблемы.

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();
    }
}
30 голосов
/ 03 февраля 2009

Из " Класс ZipPackage " (MSDN):

Хотя пакеты хранятся в виде Zip-файлов * через класс ZipPackage, все Zip-файлы не являются ZipPackages. ZipPackage предъявляет особые требования, такие как имена файлов (частей), совместимых с URI, и файл «[Content_Types] .xml», который определяет типы MIME для всех файлов, содержащихся в пакете. Класс ZipPackage нельзя использовать для открытия произвольных файлов Zip, которые не соответствуют стандарту Open Packaging Conventions.

Подробнее см. В разделе 9.2 «Отображение в ZIP-архив» международного стандарта ECMA «Открытые упаковочные соглашения», http://www.ecma -international.org / публикации / файлы / ECMA-ST / Office% 20Open% 20XML% 20Part% 202% 20 (DOCX) .zip (342Kb) или http://www.ecma -international.org / публикации / файлы / ECMA-ST / Office% 20Open% 20XML% 20Part% 202% 20 ( PDF) .zip (1,3 МБ)

* Вы можете просто добавить «.zip» к расширению любого файла на основе ZipPackage (.docx, .xlsx, .pptx и т. Д.), Чтобы открыть его в своей любимой утилите Zip.

13 голосов
/ 09 апреля 2012

У меня была точно такая же проблема! Чтобы заставить метод GetParts () что-то вернуть, мне пришлось добавить файл [Content_Types] .xml в корень архива с узлом «По умолчанию» для каждого включенного расширения файла. После того, как я добавил это (только с помощью Windows Explorer), мой код смог прочитать и извлечь заархивированное содержимое.

Более подробную информацию о файле [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();
        } 
    } 
} 

Примечание: этот код использует метод Stream.CopyTo в .NET 4.0

6 голосов
/ 10 февраля 2009

Я согласен с Cheeso. System.IO.Packaging неудобен при обработке типовых zip-файлов, поскольку он разработан для документов Office Open XML. Я бы предложил использовать DotNetZip или SharpZipLib

1 голос
/ 21 августа 2012

(Это в основном перефразировка этого ответа )

Оказывается, что System.IO.Packaging.ZipPackage не поддерживает PKZIP, поэтому при открытии «общего» ZIP-файла «части» не возвращаются. Этот класс поддерживает только некоторые специфические разновидности ZIP-файлов (см. Комментарии внизу MSDN-описание ), используемые среди прочих как пакеты служб Windows Azure до SDK 1.6 - поэтому, если вы распакуете пакет службы, а затем перепакуете если он использует упаковщик Info-ZIP, он станет недействительным.

...