Извлечение файлов из архива Zip программным способом с использованием C# и System.IO.Packaging
У меня есть куча 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
(или получитьчто-нибудь по этому вопросу). Кажется, что пакет, пока открыт, пуст. Копая глубже, отладчик показывает, что закрытый член _zipArchive показывает, что у него действительно есть части. Запчасти с правильными именами и все. Почему не GetParts
функция их получения? Я пытался открыть ZipArchive, но это не помогло. Хмм.
6 ответов
Если вы манипулируете 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 был прекращен и заархивирован
Из 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();
}
}
Из " ZipPackage Class" (MSDN):
Хотя пакеты хранятся в виде Zip-файлов * через класс ZipPackage, все Zip-файлы не являются ZipPackages. У ZipPackage есть особые требования, такие как имена файлов (частей), совместимых с URI, и файл "[Content_Types].xml", который определяет типы MIME для всех файлов, содержащихся в пакете. Класс ZipPackage нельзя использовать для открытия произвольных файлов Zip, которые не соответствуют стандарту Open Packaging Conventions.
Дополнительные сведения см. В разделе 9.2 "Отображение в ZIP-архив" стандарта ECMA International "Open Packaging Conventions", http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(DOCX).zip (342Кб) или http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(PDF).zip (1.3Mb)
* Вы можете просто добавить ".zip" к расширению любого файла на основе ZipPackage (.docx, .xlsx, .pptx и т. Д.), Чтобы открыть его в своей любимой утилите Zip.
У меня была точно такая же проблема! Чтобы метод 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
Я согласен с Cheeso. System.IO.Packaging неудобен при обработке типовых zip-файлов, поскольку он разработан для документов Office Open XML. Я бы предложил использовать DotNetZip или SharpZipLib
(Это в основном перефразировка этого ответа)
Получается что System.IO.Packaging.ZipPackage
не поддерживает PKZIP, поэтому при открытии "общего" ZIP-файла "части" не возвращаются. Этот класс поддерживает только некоторые специфические разновидности ZIP-файлов (см. Комментарии в нижней части описания MSDN), используемые, помимо прочего, в качестве пакетов служб Windows Azure до SDK 1.6 - поэтому, если вы распаковываете пакет служб, а затем перепаковываете его, скажем, Info-ZIP упаковщик станет недействительным.