C# Extract XML from Zip to MemoryStream

Magic1416

Lieutenant
Registriert
Dez. 2003
Beiträge
513
Hi,

ich möchte ohne Umweg über die Festplatte ein XML File aus einem ZIP File extrahieren und über einen XMLReader direkt mit diesem Stream befüttern. Mit Umweg über das FileSystem geht es. Nehme ich den Stream her steht nur Müll drin. Ich nutze die freie Bibliothek ICSharpCode.SharpZipLib

hier der Beispiel Code:

Umweg übers FileSytem, welcher funktioniert:

Code:
private static void extractZipFile(string archiveFilenameIn, string password, string outFolder, string fileToExtract)
        {
            ZipFile zf = null;
            try
            {
                FileStream fs = File.OpenRead(archiveFilenameIn);
                zf = new ZipFile(fs);
                if (!String.IsNullOrEmpty(password))
                {
                    zf.Password = password;     
                }
                foreach (ZipEntry zipEntry in zf)
                {
                    if (!zipEntry.IsFile)
                    {
                        continue;         
                    }



                    string entryFileName = zipEntry.Name;
                    if (!string.IsNullOrEmpty(fileToExtract))
                    {
                        if (fileToExtract.ToLower() != entryFileName.ToLower())
                            continue;
                    }

                    Stream zipStream = zf.GetInputStream(zipEntry);

                    // Manipulate the output filename here as desired.
                    string fullZipToPath = Path.Combine(outFolder, entryFileName);
                    string directoryName = Path.GetDirectoryName(fullZipToPath);
                    if (directoryName.Length > 0)
                        Directory.CreateDirectory(directoryName);

                    
                    using (FileStream streamWriter = File.Create(fullZipToPath))
                    {
                        StreamUtils.Copy(zipStream, streamWriter, new byte[4096]);  // 4K buffer is optimum
                    }

                }
            }
            finally
            {
                if (zf != null)
                {
                    zf.IsStreamOwner = true; // Makes close also shut the underlying stream
                    zf.Close(); // Ensure we release resources
                }
            }
        }


Und nun der Code der den MemoryStream zurückgeben soll, welcher nicht funktioniert, aber mein bevorzugter Weg werden soll:

Code:
public static Stream ExtractZipFileToStream(string archiveFilenameIn, string password, string fileToExtract)
        {
            ZipFile zf = null;
            try
            {
                FileStream fs = File.OpenRead(archiveFilenameIn);
                zf = new ZipFile(fs);
                if (!String.IsNullOrEmpty(password))
                {
                    zf.Password = password;     
                }
                foreach (ZipEntry zipEntry in zf)
                {
                    if (!zipEntry.IsFile)
                    {
                        continue;           
                    }

                    string entryFileName = zipEntry.Name;
                    
                    if (!string.IsNullOrEmpty(fileToExtract))
                    {
                        if (fileToExtract.ToLower() != entryFileName.ToLower())
                            continue;
                    }

                    Stream zipStream = zf.GetInputStream(zipEntry);
                    MemoryStream outputStream = new MemoryStream();
                 

                    StreamUtils.Copy(zipStream, outputStream, new byte[4096]);  // 4K buffer is optimum

                    return outputStream;

                }
            }
            finally
            {
                if (zf != null)
                {
                    zf.IsStreamOwner = true; // Makes close also shut the underlying stream
                    zf.Close(); // Ensure we release resources
                }
            }
            return null;
        }

Wo liegt der Fehler an der Variante mit dem MemoryStream ? Kontrolliert habe ich es, indem ich den MemoryStream wieder auf Platte geschrieben habe. Nicht ein einziges Byte stimmt.

Danke, Gruß Magic
 
Code:
var file = File.OpenRead( @"E:\file.zip" );
ZipFile zip = new ZipFile( file );
ZipEntry entry = zip.GetEntry( "file.xml" );
var stream = zip.GetInputStream( entry );
XDocument doc = XDocument.Load( stream );
// ...
 
Es sei noch erwähnt, dass es bei großen Archiven (>10MB) oft zu MemoryLeaks kommen kann wenn alles im RAM passiert.
Denn hierbei wird ja erstmal das Zip-Archiv in den RAM geladen, dann wird es entpackt (also vielfach mehr Platz als das gezippte Archiv) und dann aus dem XML die Objektstruktur gebaut, die ja auch Platz braucht.
Da dass schnell mehr Speicher im Memory braucht als zur Verfügung steht, wird dann natürlich ausgelagert auf die Platte und das ist teuer.
Daher in solchen Fällen lieber das Archiv auf einen temporären Ort auf die Platte extrahieren und dann nur das XML einlesen.

Übrigens passiert das mit den MemoryLeaks häufig schon beim Generieren des Zip-Archives, wenn alles im Memory geschieht...auch hier lieber erst die ungezippten Dateien auf Platte schreiben in den Temps und dann von da zippen.
 
Hi holy,

Danke für die Hilfe, das funktioniert. Nur verstehe ich nicht warum.
Ich dachte, das eigentliche Entpacken passiert mit folgendem Befehl

Code:
StreamUtils.Copy(zipStream, outputStream, new byte[4096]);

Warum ist dieser dann beim File erstellen wichtig ? Mein Code basiert zu 60 % auf deren Beispiel, daher die Frage.

Außerdem musste noch folgendes Codeschnipsel raus damit es dann wirklich lief.

Code:
if (zf != null)
                {
                    zf.IsStreamOwner = true; // Makes close also shut the underlying stream
                    zf.Close(); // Ensure we release resources
                }
Ergänzung ()

@Erdmänchen:

Danke für die Info.
Das wird in meinem Fall nicht passieren. Es ist eine Konfigurationsdatei, ein unbenanntes Zip File (wie z.B. das .docx Format) welches mehrere XML Files enthält. Nur eine einziges XML File da drin ist wichtig für das was ich programmieren möchte. Die Größe des Archivs wird 5 MB kaum überschreiten.
Den Weg ohne Umweg der direkten Auslagerung finde ich einfach schöner.

Gruß Magic
 
Zuletzt bearbeitet:
Zurück
Oben