2012-04-26 12 views
5

Sto provando a progettare una semplice applicazione da utilizzare per il calcolo di un file CRC32/md5/sha1/sha256/sha384/sha512, e ho incontrato un po 'di un roadblock. Questo è stato fatto in C#.Come hash un singolo file in più modi contemporaneamente?

Mi piacerebbe essere in grado di farlo nel modo più efficiente possibile, quindi il mio pensiero originale era quello di leggere il file in un memorystream prima di elaborare, ma ho presto scoperto che file molto grandi mi causano l'esaurimento della memoria molto velocemente. Quindi sembrerebbe che io debba usare un filestream. Il problema, a mio avviso, è che è possibile eseguire solo una funzione di hash alla volta, e così facendo con un filestream ci vorrà un po 'per completare ogni hash.

Come potrei andare a leggere un piccolo frammento di un file in memoria, elaborarlo con tutti e 6 gli algoritmi e poi andare su un altro blocco ... O l'hashing non funziona in questo modo?

Questo era il mio tentativo originale di leggere un file in memoria. Non è riuscito quando ho provato a leggere un'immagine CD in memoria prima di eseguire gli algoritmi di hashing sulla MemoryStream: algoritmi

private void ReadToEndOfFile(string filename) 
    { 
     if (File.Exists(filename)) 
     { 
      FileInfo fi = new FileInfo(filename); 
      FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read); 
      byte[] buffer = new byte[16 * 1024]; 

      //double step = Math.Floor((double)fi.Length/(double)100); 

      this.toolStripStatusLabel1.Text = "Reading File..."; 
      this.toolStripProgressBar1.Maximum = (int)(fs.Length/buffer.Length); 
      this.toolStripProgressBar1.Value = 0; 

      using (MemoryStream ms = new MemoryStream()) 
      { 
       int read; 
       while ((read = fs.Read(buffer, 0, buffer.Length)) > 0) 
       { 
        ms.Write(buffer, 0, read); 
        this.toolStripProgressBar1.Value += 1; 
       } 

       _ms = ms; 
      } 
     } 
    } 

risposta

3

Sei quasi tutto lì, non hai bisogno di leggere l'intera cosa in memoria in una sola volta.

Tutti gli hash in .Net derivano dalla classe HashAlgorithm. Questo ha due metodi: TransformBlock e TransformFinalBlock. Quindi, dovresti essere in grado di leggere un chunk per il tuo file, inserirlo nel metodo TransformBlock di qualsiasi hash che vuoi usare e poi spostarti nel blocco successivo. Basta ricordare di chiamare TransformFinalBlock per l'ultimo blocco dal file, in quanto è ciò che ottiene l'array di byte contenente l'hash.

Per ora, vorrei solo fare ogni hash uno alla volta, fino a quando non funziona, poi preoccuparsi di rimanere a corto gli hash contemporaneamente (usando qualcosa come la Biblioteca Task Parallel)

+0

Ho provato a farlo funzionare con MD5 e il programma viene eseguito, anche se sembra che generi hash non corretti. Ecco un collegamento al mio codice: [collegamento] (http://pastebin.com/i3iPwYZv) – agent154

+1

Si dovrebbe usare 'read' invece di' buffer.Length' quando si chiama 'TransformFinalBlock' –

+0

Grazie mille! La notte scorsa stavo tormentando per un po '. Finito per hacking insieme qualcosa di stupido cercando di farlo funzionare, ma non ho potuto fare a meno di sentire che non era necessario. Ho scoperto che era perché l'ultimo array veniva letto completamente anche quando l'ultimo blocco era troppo piccolo per questo. Ho finito per creare un nuovo array di byte per l'ultimo pezzo a parità di dimensioni dell'ultimo blocco. – agent154

4

Hash sono progettati in un modo che è possibile calcolare il valore hash in modo incrementale. È possibile trovare un esempio C# /. NET per quello here. È possibile modificare facilmente il codice fornito per aggiornare più istanze di algoritmo hash in ogni passaggio.

0

Questa potrebbe essere una grande opportunità per ottenere i piedi bagnati con gli oggetti del flusso di dati TPL. Leggi il file in una discussione e pubblica i dati su BroadcastBlock<T>. Lo BroadcastBlock<T> sarà collegato a 6 diverse istanze ActionBlock<T>. Ogni ActionBlock<T> corrisponderà a una delle tue 6 strategie di hash.

var broadcast = new BroadcastBlock<byte[]>(x => x); 

var strategy1 = new ActionBlock<byte[]>(input => DoHash(input, SHA1.Create())); 
var strategy2 = new ActionBlock<byte[]>(input => DoHash(input, MD5.Create())); 
// Create the other 4 strategies. 

broadcast.LinkTo(strategy1); 
broadcast.LinkTo(strategy2); 
// Link the other 4. 

using (var fs = File.Open(@"yourfile.txt", FileMode.Open, FileAccess.Read)) 
using (var br = new BinaryReader(fs)) 
{ 
    while (br.PeekChar() != -1) 
    { 
    broadcast.Post(br.ReadBytes(1024 * 16)); 
    } 
} 

Il BroadcastBlock<T> vi invierà ogni blocco di dati da tutti legati ActionBlock<T> istanze.

Poiché la domanda si è concentrata maggiormente su come ottenere tutto ciò in modo simultaneo, lascerò a voi l'implementazione di DoHash.

private void DoHash(byte[] input, HashAlgorithm algorithm) 
{ 
    // You will need to implement this. 
} 
+0

Questo sembra un approccio molto interessante al multithreading. Peccato che sia in .net 4.5. Per qualche ragione, ho già abbastanza tempo per convincermi ad usare .net 4.0, dato che non mi sembra ancora abbastanza mainstream. – agent154

+0

Sembra che questo approccio non funzionerà. DoHash verrà chiamato per ogni array di input di byte. Come dovrebbero essere combinati? – Petro

Problemi correlati