2017-07-20 4 views
0

Ich schreibe ein wenig C# Appx Paket-Editor (appx ist im Grunde eine Zip-Datei mit einer Reihe von XML-Metadaten-Dateien).Wie bekomme ich die komprimierte Größe (ZipArchive) eines FileStream-Blocks?

Um eine gültige appx Datei zu machen, ich brauche einen Block Map-Datei (XML), die für jede Datei zwei Attribute enthält erstellen: hash und Größe wie hier erklärt (https://docs.microsoft.com/en-us/uwp/schemas/blockmapschema/element-block)

Hash repräsentieren ein 64kb unkomprimiert Stück einer Datei. Größe repräsentiert die Größe dieses Chunks nach der Komprimierung (Deflate-Algorithmus). Hier ist, was ich bisher als Proof of Concept hat geschrieben:

using System; 
using System.IO; 
using System.IO.Compression; 
using System.Linq; 

namespace StreamTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var srcFile = File.OpenRead(@"C:\Test\sample.txt")) 
      { 
       ZipAndHash(srcFile); 
      } 

     Console.ReadLine(); 
    } 

    static void ZipAndHash(Stream inStream) 
    { 
     const int blockSize = 65536; //64KB 
     var uncompressedBuffer = new byte[blockSize]; 
     int bytesRead; 
     int totalBytesRead = 0; 

     //Create a ZIP file 
     using (FileStream zipStream = new FileStream(@"C:\Test\test.zip", FileMode.Create)) 
     { 
      using (ZipArchive zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create)) 
      { 
       using (BinaryWriter zipWriter = new BinaryWriter(zipArchive.CreateEntry("test.txt").Open())) 
       { 
        //Read stream with a 64kb buffer 
        while ((bytesRead = inStream.Read(uncompressedBuffer, 0, uncompressedBuffer.Length)) > 0) 
        { 
         totalBytesRead = totalBytesRead + bytesRead; 

         //Compress current block to the Zip entry 
         if (uncompressedBuffer.Length == bytesRead) 
         { 
          //Hash each 64kb block before compression 
          hashBlock(uncompressedBuffer); 

          //Compress 
          zipWriter.Write(uncompressedBuffer); 
         } 
         else 
         { 
          //Hash remaining bytes and compress 
          hashBlock(uncompressedBuffer.Take(bytesRead).ToArray()); 
          zipWriter.Write(uncompressedBuffer.Take(bytesRead).ToArray()); 
         } 
        } 

        //How to obtain the size of the compressed block after invoking zipWriter.Write() ? 

        Console.WriteLine($"total bytes : {totalBytesRead}"); 
       } 
      } 
     } 

    } 

    private static void hashBlock(byte[] uncompressedBuffer) 
    { 
     // hash the block 
    } 
    } 
} 

ich einfach den Hash-Attribut unter Verwendung eines 64 kb-Puffer erhalten kann, während ein Stream zu lesen, meine Frage ist:

Wie erhalte ich die komprimierte Größe von jedem 64kb-Block nach der Verwendung von zipWrite.Write(), ist es überhaupt möglich mit System.IO.Compression oder sollte ich etwas anderes verwenden?

+0

Können Sie es aus dem ZipArchive-Objekt abrufen? So etwas wie zipArchive.GetEntry ("test.txt"). CompressedLength – wakers01

+0

Leider nicht: NotSupportedException Das Zip-Archiv unterstützt das Lesen nicht, wahrscheinlich weil der Eintrag noch nicht komplett geschrieben ist. – Coloris

Antwort

0

Wenn Ihr Problem noch aktuell ist, für Behälter zu schaffen Sie 2 Ansatz verwenden:

  1. Managed OPC Verpackungs APIs, die für Anwendungen unterstützen, die Open Packaging Conventions kompatible Dateien, die so genannte Pakete erzeugen oder verbrauchen, und entwickeln alle spezifischen Dinge selbst (allgemeine Beschreibung ist hier: https://msdn.microsoft.com/en-us/library/windows/desktop/dd371623(v=vs.85).aspx)

Für Blöcke Kompression Größe immer on-the-fly Sie DeflateStream und Memory wie unten verwenden:

 private static long getCompressSize(byte[] input) 
    { 

     long length = 0; 

     using (MemoryStream compressedStream = new MemoryStream()) 
     { 
      compressedStream.Position = 0; 

      using (DeflateStream compressionStream = new DeflateStream(compressedStream, CompressionLevel.Optimal, true)) 
      { 
       compressionStream.Write(input, 0, input.Length); 

      } 


      length = compressedStream.Length; 

     } 
     Logger.WriteLine("input length:" + input.Length + " compressed stream: " + length); 
     return length; 
    } 
  1. C++ API für Appx Container (aber in diesem Fall Projekt sollte auf C# Projekt geschrieben und importiert wird unter Verwendung von C neu geschrieben werden ++ oder zusätzliche Bibliothek soll). Der Hauptvorteil ist, dass es bereits Methoden zum Erstellen aller benötigten Paketteile hat. Allgemeine Beschreibung ist hier (https://msdn.microsoft.com/en-us/library/windows/desktop/hh446766(v=vs.85).aspx)

Lösung für Blockgröße immer und Hash:

Die IAppxBlockMapBlock-Schnittstelle bietet ein Nur-Lese-Objekt, das einen einzelnen Block in einer Datei in der Block-Kartendatei enthalten repräsentiert (AppxBlockMap.xml) für das App-Paket. Die IAppxBlockMapFile :: GetBlocks-Methode wird zum Zurückgeben eines Enumerators zum Versetzen und Abrufen der einzelnen Blöcke einer Datei verwendet, die in der Paketblockzuordnung aufgelistet sind.

IAppxBlockMapBlock Die Schnittstelle IUnknown erbt von der Schnittstelle und verfügt über die folgenden Methoden:

GetCompressedSize - Ermittelt komprimierte Größe des Blocks.

GetHash - Ruft den Hashwert des Blocks ab.

+0

@Eugene Ich habe gerade Ihre Antwort bearbeitet, um ungewollte Interpretationen zu vermeiden. Ein Tipp ist auch, die relevanten Elemente aus den Links, die Sie zur Verfügung stellen, hinzuzufügen, damit Ihre Antwort auch dann hilfreich ist, wenn sich die Links ändern. –

+0

@ André danke. Detailliertere Antwort für bessere Wahrnehmung hinzugefügt. Aber ich denke immer noch, dass Links notwendig sind, denn hinter ihnen können Leute allgemeine API-Beschreibung finden. – Eugene

Verwandte Themen