www.rolandk.de
- Aktuelle Themen zu .Net -
Achtung: Hier handelt es sich um meine alte Seite.
Die aktuelle ist unter folgendem Link erreichbar: www.rolandk.de/wp/
Home Artikel .Net Allgemein Packet-Dateien mit .Net Standardmitteln




















































Packet-Dateien mit .Net Standardmitteln
Sonntag, den 13. Februar 2011 um 11:52 Uhr

 

 

Allgemeines

Packet-Dateien werden heute von vielen Programmen genutzt, um etwa verschiedene Dateien zusammenfassend in eine einzige Datei zu speichern. Ein sehr gutes Beispiel sind die aktuellen Office-Produkte von Microsoft. Jede Excel-, Word- oder Powerpoint-Datei ist in Wirklichkeit nichts anderes als eine Zip-Datei, in der verschiedene Dateien gespeichert sind. Als Beweis reicht es, eine Office-Datei mit WinZIP oder WinRAR zu öffnen. Nachfolgender Screenshot zeigt eine Word-Datei geöffnet mit WinRAR:

 

 

Seit .Net 3.0 gibt es den Namensraum System.IO.Packaging in der WindowsBase.dll. Dieser Namensraum erlaubt das Lesen, Schreiben und Verändern von Packet-Dateien nach dem sog. Open Packaging Format. Das Open Packaging Format beschreibt grundsätzliche Strukturen, wie auf Packete zugegriffen werden soll oder kann. Standardmäßig wird hierbei im .Net Framework auch das Zip Format unterstützt. Der Entwickler muss aber eines wissen: Jedes Packet nach dem Open Packaging Format kann in eine Zip-Datei geschrieben werden, jede Zip-Datei ist aber nicht automatisch nach dem Open Packaging Format aufgebaut. Will man also auf generell alle möglichen Zip-Dateien zugreifen, sind die Klassen dieses Namensraums der falsche Weg. In diesem Artikel will ich einige Funktionen des System.IO.Packaging Namensraum anhand von Beispielen mit Zip-Dateien vorstellen.

 

Packet-Dateien erzeugen

Eine Zip-Datei zu erzeugen ist im Grunde recht einfach. Im Framework gibt es dazu die Klasse ZipPackage. ZipPackage bildet den Zugriff auf eine komplette Zip-Datei ab, alle innerhalb der Zip-Datei verpackten Dateien dagegen werden über jeweilige PackagePart Objekte abgebildet. Im nachfolgenden Beispiel wird eine vorhandene Datei (Parameter fileName) in eine neu erzeugte Zip-Datei mit dem gleichen Namen verpackt.

  1. /// <summary>
  2. /// Starts to compress the given file.
  3. /// </summary>
  4. /// <param name="fileName">The file to compress.</param>
  5. public static void CompressFile(string fileName)
  6. {
  7. //Check given file name
  8. if (!File.Exists(fileName))
  9. {
  10. throw new FileNotFoundException(
  11. "Unable to find the file that should be compressed!", fileName);
  12. }
  13.  
  14. //Generate name for new zip file
  15. string zipFileName = GenerateNewZipName(fileName);
  16. if (File.Exists(zipFileName))
  17. {
  18. throw new ApplicationException(
  19. "Zip-File does already exist (" + zipFileName + ")!");
  20. }
  21.  
  22. //Generate zip package and a part for our file
  23. using (ZipPackage zipPackage = ZipPackage.Open(zipFileName, FileMode.Create)
  24. as ZipPackage)
  25. {
  26. PackagePart part = zipPackage.CreatePart(
  27. new Uri("/" + Path.GetFileName(fileName), UriKind.Relative),
  28. "text/text",
  29. CompressionOption.Normal);
  30.  
  31. //Copy file into zip part
  32. using (Stream fileStream = part.GetStream(FileMode.Create))
  33. {
  34. using (Stream sourceFileStream = File.OpenRead(fileName))
  35. {
  36. CopyBytes(sourceFileStream, fileStream);
  37. sourceFileStream.Close();
  38. }
  39.  
  40. //Close generated zip file
  41. fileStream.Close();
  42. }
  43. zipPackage.Close();
  44. }
  45. }

Die wichtigsten Zeilen in diesem Beispiel sind folgende:

  1. //Generate zip package and a part for our file
  2. ZipPackage zipPackage = ZipPackage.Open(zipFileName, FileMode.Create)
  3. as ZipPackage;
  4. PackagePart part = zipPackage.CreatePart(
  5. new Uri("/" + Path.GetFileName(fileName), UriKind.Relative),
  6. "text/text",
  7. CompressionOption.Normal);

Im ersten Schritt wird hier eine Zip-Datei mit einem beliebigen Namen erzeugt. Der zweite Parameter verhält sich genauso, wie bei der Arbeit mit der File-Klasse gewohnt. In diesem Beispiel hier soll also eine Datei erzeugt werden, für den Fall, dass es die Datei schon gibt, wird sie einfach überschrieben. Als nächstes wird eine PackagePart für die zu verpackende Datei angelegt. Hierbei sind zwei Dinge zu beachten:

  • Das Format der Uri muss den Open Packaging Conventions entsprechen (Siehe Msdn). Unterm Strich heißt dass, dass relative Pfade mit "/" beginnen müssen, alles weitere ist im Grunde wie vom normalen Dateisystem gewohnt.
  • Es muss ein Mime-Typ angegeben werden, es wird allerdings nicht geprüft, ob der Inhalt der Datei wirklich dem angegebenen Mime-Typ entspricht.

Im Anschluss dazu wird im Beispiel der Inhalt der Datei vom Dateisystem aus in das PackagePart-Objekt kopiert und danach die Zip-Datei geschlossen. Dieser Vorgang kann natürlich für beliebig viele Dateien wiederholt werden.

 

Ein kurzer Blick in die so erstellte Zip-Datei birgt eine kleine Überraschung: Neben der Datei, die wir hinein kopiert haben, wurde auch eine zusätzliche Datei namens [Content_Types].xml erzeugt. Bei mir steht in dieser Datei zum Beispiel folgendes:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
  3. <Default Extension="h" ContentType="text/text" />
  4. </Types>

Wie man sieht, werden in dieser Datei alle in dem Packet enthaltenen Dateitypen beschrieben. Das Vorhandensein dieser Datei ist durch das Open Packaging Format vorgeschrieben, man kann also auch nicht verhindern, dass diese Datei erzeugt wird.

 

Packet-Dateien auslesen

Das Auslesen von Packet-Dateien funktioniert denkbar einfach. Mit der nachfolgenden Methode können alle Dateien eines Packets (Parameter sourceZipFile) in einen Ordner (Parameter targetDirectory) extrahiert werden.

  1. /// <summary>
  2. /// Extracts all files from the given zip-file to the given directory.
  3. /// </summary>
  4. /// <param name="sourceZipFile">The source zip file.</param>
  5. /// <param name="targetDirectory">The target directory.</param>
  6. public static void ExtractAllFiles(string sourceZipFile, string targetDirectory)
  7. {
  8. using (ZipPackage zipPackage = ZipPackage.Open(sourceZipFile, FileMode.Open)
  9. as ZipPackage)
  10. {
  11. foreach (PackagePart actPart in zipPackage.GetParts())
  12. {
  13. //Open part for reading
  14. using (Stream partReadStream = actPart.GetStream(FileMode.Open))
  15. {
  16. //Generate and write the file on the file-system
  17. string newFileName = Path.Combine(
  18. targetDirectory,
  19. actPart.Uri.OriginalString.TrimStart('/'));
  20. string directoryName = Path.GetDirectoryName(newFileName);
  21. if (!Directory.Exists(directoryName))
  22. {
  23. Directory.CreateDirectory(directoryName);
  24. }
  25. using (Stream fileStream = File.Create(newFileName))
  26. {
  27. CopyBytes(partReadStream, fileStream);
  28. fileStream.Close();
  29. }
  30.  
  31. //Close part stream
  32. partReadStream.Close();
  33. }
  34. }
  35. }
  36. }

Über die Methode GetParts kann eine Auflistung aller gefundenen Dateien eines Packets abgefragt werden. Über die damit erhaltenen PackagePart Objekte können die Dateien und verschiedene Eigenschaften wie der Name ausgelesen werden. Wichtig an dieser Stelle ist die Tatsache, dass nur solche Packete ausgelesen werden können, die nach dem Open Packaging Format aufgebaut sind. Fehlt etwa die weiter oben beschriebene [Content_Types].xml, so gibt die Methode GetParts immer eine leere Auflistung zurück.

 

Packet-Dateien verändern

Zum verändern von wurden im Prinzip schon alle nötigen Schritte behandelt. Nachfolgende Methode fügt einer vorhandenen Zip-Datei (Parameter targetZip) eine weitere Datei hinzu (Parameter sourceFile):

  1. /// <summary>
  2. /// Adds the given file to the given zip file.
  3. /// </summary>
  4. /// <param name="sourceFile">File to add to the zip file.</param>
  5. /// <param name="targetZip">Target zip file to write the given file in.</param>
  6. public static void AddToZip(string sourceFile, string targetZip)
  7. {
  8. //Check given file name
  9. if (!File.Exists(sourceFile))
  10. {
  11. throw new FileNotFoundException(
  12. "Unable to find the file that should be compressed!", sourceFile);
  13. }
  14. string sourceFileName = Path.GetFileName(sourceFile);
  15.  
  16. //Open the zip package
  17. using(ZipPackage zipPackage = ZipPackage.Open(targetZip, FileMode.Open)
  18. as ZipPackage)
  19. {
  20. //Check if there is already a file with this name
  21. if (zipPackage.PartExists(new Uri("/" + sourceFileName, UriKind.Relative)))
  22. {
  23. throw new ApplicationException(
  24. "There is already a file with this name within the zip!");
  25. }
  26.  
  27. //Create the new part
  28. PackagePart part = zipPackage.CreatePart(
  29. new Uri("/" + Path.GetFileName(sourceFileName), UriKind.Relative),
  30. "text/text",
  31. CompressionOption.Normal);
  32.  
  33. //Copy file into zip part
  34. using (Stream fileStream = part.GetStream(FileMode.Create))
  35. {
  36. using (Stream sourceFileStream = File.OpenRead(sourceFile))
  37. {
  38. CopyBytes(sourceFileStream, fileStream);
  39. sourceFileStream.Close();
  40.  
  41. //Close generated zip file
  42. fileStream.Close();
  43. }
  44. }
  45. zipPackage.Close();
  46. }
  47. }

 

Zusammenfassung

Mit dem Namensraum System.IO.Packaging bietet das .Net Framework eine einfache Möglichkeit, eigene Packet-Dateien zu erzeugen und auszulesen. Aufgrund des Zip-Formats können so erzeugte Dateien auch Problemlos von anderen Programmen geöffnet werden. Es ist allerdings wichtig zu wissen, dass nicht automatisch alle Zip-Dateien mit den Klassen dieses Namensraums ausgelesen werden können. Diese Tatsache liegt der Verwendung der von Microsoft definierten Open Packaging Conventions zugrunde.

 

Quellen

  • Msdn
    Allgemeine Dokumentation auf Msdn.
  • Msdn
    Beschreibung des Adressierungsmodells.
 

Kommentar hinzufügen

Ihr Name:
Kommentar:

Kommentare (1)

1. Steffen
Mittwoch, den 02. August 2017 um 06:35 Uhr
Sehr hilfreich dieser Beitrag super