Feedback

C# - Datei aus einem Zip-Archiv entpacken z.B. Zip, 7z, Rar usw.

Veröffentlicht von am 14.10.2009
(5 Bewertungen)
Ich habe nach einer einfachen Möglichkeit gesucht, eine Datei aus einem gepackten Arichv in einem Stream zu extrahieren und die Datei dann auszulesen.

Dazu habe ich eine einfache Schnittstelle erstellt - IArchive - über die man verschiedene Zip Bibliotheken implementieren kann. In diesem Beispiel habe ich das .NET Binding für 7zip bentuzt. Zu finden auf http://sevenzipsharp.codeplex.com/. Auf dieser Seite werden auch im Downloadbereich die Assembly SevenZipSharp.dll sowie die 7z.dll benötigt.

Zum Projekt muss die SevenZipSharl.dll als Referenz hinzugefügt werden und die 7z.dll ins Anwendungsverzeichnis kopiert werden.

IArchive besitzt eine einfache Funktion GetStream, die als ersten Parameter den relativen Pfad zur Datei im Archiv bekommt und als Zweiten einen Lambdaausdruck, der die Strategie zum Entpacken angibt. Von der Lambda Funktion wird eine von Stream abgeleitete Klasse zurückgegeben, die vom darunter liegenden Archivierungsprogram (in meinem Fall SevenZipSharp) benutzt wird, um die Daten zu entpacken.

In dem Beispiel wird ein MemoryStream verwendet, um einen schnellen Datenzugriff zu erhalten.

Ein weiterer Kandidaten zur Implementierung eines Packprogramms wäre DotNetZip http://dotnetzip.codeplex.com/ oder aus dem SharpDevelop Projekt die SharpZipLib http://sharpdevelop.net/OpenSource/SharpZipLib/

Last but not least, wäre eine einfache Implementierung und Kapselung von dem OpenXML SDK darunter vorstellbar.

Der Unten angefügte Code ist mit einem Beispielklasse zur Verwendung (MyApp) angegeben.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using SevenZip;

public class MyApp
    {    	
		public MyApp()
		{
			var archivePath = @"C:\Users\Rainer\Downloads\dev\boo-0.9.1.3287-bin.zip";
			var compressedArchive = new CompressedArchive(archivePath);
			var settingsStream = getSettingsStreamFromArchive(compressedArchive);
			var xmlSettingsDoc = createXmlDocumentFromStream(settingsStream);
			configureAppFromXDocument(xmlSettingsDoc);
		}
		
		private XDocument createXmlDocumentFromStream(Stream stream)
		{
			var xmlReader = XmlReader.Create(stream);
			return XDocument.Load(xmlReader);
		}
		
		private static Stream getSettingsStreamFromArchive(IArchive archive)
		{        	
			var relativePathInArchive = @"extras\rules.xml";
			var settingsStream = archive.GetStream(relativePathInArchive, () => new MemoryStream() );
			return settingsStream;
		}
    }
    
	public interface IArchive
	{
		Stream GetStream(string pathToExtract, Func<Stream> outputStreamingStrategy);
	}
    
    public class CompressedArchive : IArchive
    {	
    	private readonly SevenZipExtractor zipExtractor;
    	
    	public CompressedArchive(string archivePath)
    	{
    		zipExtractor = new SevenZipExtractor(archivePath);
    	}
    	
		public Stream GetStream(string pathToExtract, Func<Stream> outputStreamingStrategy)
		{
			if (outputStreamingStrategy == null)
				throw new ArgumentNullException("outputStreamingStrategy");
			if (pathToExtract == null)
				throw new ArgumentNullException("pathToExtract");
			
           var outputStream = outputStreamingStrategy();
			zipExtractor.ExtractFile(pathToExtract, outputStream );
			//in order to be used by another wrapping class (i.e. XmlReader), the stream has to be reset
			outputStream.Position = 0;
			return outputStream;				
		}
    }
Abgelegt unter Zip, Archiv, Rar, 7zip, entpacken.

4 Kommentare zum Snippet

Rainer Hilmer schrieb am 14.10.2009:
Sauberster Code, darum eine glatte 10 von mir.
Rene Drescher-Hackel schrieb am 19.10.2009:
var xmlSettingsStream = getXmlSettingsFromArchive(archive);
Wo kommt "archive" her? Und warum "var xmlSettingsStream" wenn "getXmlSettingsFromArchive" bekanntermaßen einen Stream zurück gibt?
Was hats mit dieser Zeile : "var relativePathInArchive = @"extras\rules.xml";
" auf sich - das soll doch eine Datei aus dem Archive sein - wieso dann hart codiert?
Rainer Schuster schrieb am 22.10.2009:
Hallo Rene,

kann ich dir leicht erklären. Ich habe das snippet refaktorisiert und beim Speichern hat es das erste mal nicht geklappt. Das hatte ich nicht gegen geprüft. Jetzt hab ich die offensichtlichen Fehler korigiert.

archive kommt von CompressedArichve (heißt jetzt auch so).
getXmlSettingsFromArchive gab es nicht mehr (auch ein offensichtlicher Fehler). Funktion heißt getSettingsStreamFromArchive.

hart codiert, weil es ein Beispiel ist. Du willst in deiner app einen Stream aus einem Zip haben. Da ein Zip nichts anderes ein ein Kompaktes Dateisystem ist, muss du deine Daten referenzieren können. Wenn du dich um die Strukturierung nicht kümmerst, dann baue die Logik in eine Klasse, die sie kennt. Habe ich in meinem Vorliegenden Fall auch so gemacht. Wenn du also damit z.B. xdoc (spricht Word2007 Dokumente) auslesen willst, steckt die logik auch wo anders drinnen. Nicht im Archiv.

getSettingsStreamFromArchive soll als Beispiel dafür angesehen werden wie die Settings ausgelesen werden. Hier triff das Prinzip der Kapselung auf ("Tell, don't ask"). Eine Applikation sollte schon selbst wissen was sie braucht. Wäre das ein Modul, müsstes du die Abhängigkeit über einen Parameter übergeben, oder gleich aus der Funktion einen Eigene Klasse mit Logik bauen. Hoffe du kannst mir soweit folgen.

Die Architektur des Beispiels ist dabei aber erst einmal nicht von Bedeutung. Kern dieses Snippets ist das Interface IArchive und die Implementierung CompressedArchive ;-)
Rainer Schuster schrieb am 22.10.2009:
Nachtrag zu meinem Kommentar: Das Snippet heißt "Datei aus einem Zip-Archiv entpacken" und nicht "Architektur einer Applikation mit Xml Settings". Das zweitere habe ich dazu benutzt um erstes zu zeigen. Jetzt sollte auch das Beispiel funktionieren.
 

Logge dich ein, um hier zu kommentieren!