2010-01-23 11 views
31

Ho bisogno di calcolare i checksum di file piuttosto grandi (gigabyte). Questo può essere realizzato utilizzando il seguente metodo:È possibile calcolare l'hash MD5 (o altro) con letture bufferizzate?

private byte[] calcHash(string file) 
    { 
     System.Security.Cryptography.HashAlgorithm ha = System.Security.Cryptography.MD5.Create(); 
     FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read); 
     byte[] hash = ha.ComputeHash(fs); 
     fs.Close(); 
     return hash; 
    } 

Tuttavia, i file vengono normalmente scritte solo in anticipo in modo tamponata (per esempio la scrittura 32MB di alla volta). Sono così convinto di aver visto un override di una funzione di hash che mi ha permesso di calcolare un hash MD5 (o altro) nello stesso momento in cui scrivevo, cioè: calcolare l'hash di un buffer, quindi alimentare l'hash risultante nella successiva iterazione .

Qualcosa di simile a questo: (pseudocodice-ish)

byte [] hash = new byte [] { 0,0,0,0,0,0,0,0 }; 
while(!eof) 
{ 
    buffer = readFromSourceFile(); 
    writefile(buffer); 
    hash = calchash(buffer, hash); 
} 

hash è ora sililar a quello che sarebbe stato realizzato eseguendo la funzione calcHash sull'intero file.

Ora, non riesco a trovare alcun override del genere in the.Net 3.5 Framework, sto sognando? Non è mai esistito, o sono solo pessimo a cercare? La ragione per eseguire contemporaneamente sia il calcolo della scrittura che del checksum è perché ha senso a causa dei file di grandi dimensioni.

risposta

45

Si utilizzano i metodi TransformBlock e TransformFinalBlock per elaborare i dati in blocchi.

// Init 
MD5 md5 = MD5.Create(); 
int offset = 0; 

// For each block: 
offset += md5.TransformBlock(block, 0, block.Length, block, 0); 

// For last block: 
md5.TransformFinalBlock(block, 0, block.Length); 

// Get the has code 
byte[] hash = md5.Hash; 

Nota: funziona (almeno con il provider MD5) per inviare tutti i blocchi di TransformBlock e quindi inviare un blocco vuoto per TransformFinalBlock per finalizzare il processo.

+1

omg, ho appena postato lo stesso suggerimento, usando la stessa formattazione =) –

+0

Ok, ma +1 per fornire anche un riferimento! –

+1

Ay caramba! Eccolo! Questa era la funzione che stavo cercando. Buono a sapersi non stavo inventando tutto. Grazie a Guffa e Rubens per aver fornito la risposta giusta così prontamente. A entrambi, accetterò questa risposta a causa dell'esempio di codice incluso. –

3

algoritmi hash sono attesi per gestire questa situazione e sono tipicamente implementate con 3 funzioni:

hash_init() - Chiamato per allocare le risorse e iniziare l'hash.
hash_update() - Chiamato con nuovi dati all'arrivo.
hash_final() - Completare il calcolo e risorse gratuite.

Vedere http://www.openssl.org/docs/crypto/md5.html o http://www.openssl.org/docs/crypto/sha.html per buoni esempi standard in C; Sono sicuro che ci sono librerie simili per la tua piattaforma.

+0

Buona risposta, ma il "dove è in .net?" parte della domanda rimane aperta. –

+0

@Pascal: guarda le 2 buone risposte di seguito, entrambe postate prima del tuo commento. –

4

Sembra possibile per utilizzare TransformBlock/TransformFinalBlock, come mostrato in questo esempio: Displaying progress updates when hashing large files

+0

Questo link è morto, prova invece: http://www.infinitec.de/post/2007/06/09/Displaying-progress-updates-when-hashing-large-files.aspx – Cumbayah

48

Mi piace la risposta di cui sopra, ma per ragioni di completezza, e di essere una soluzione più generale, fare riferimento alla classe CryptoStream. Se si stanno già gestendo flussi, è facile racchiudere il flusso in un CryptoStream, passando un HashAlgorithm come parametro ICryptoTransform.

var file = new FileStream("foo.txt", FileMode.Open, FileAccess.Write); 
var md5 = MD5.Create(); 
var cs = new CryptoStream(file, md5, CryptoStreamMode.Write); 
while (notDoneYet) 
{ 
    buffer = Get32MB(); 
    cs.Write(buffer, 0, buffer.Length); 
} 
System.Console.WriteLine(BitConverter.ToString(md5.Hash)); 

Potrebbe essere necessario chiudere il flusso prima di ottenere l'hash (in modo che il HashAlgorithm sa è fatto).

0

Ho appena dovuto fare qualcosa di simile, ma volevo leggere il file in modo asincrono. Sta usando TransformBlock e TransformFinalBlock e mi sta dando risposte coerenti con Azure, quindi penso che sia corretto!

private static async Task<string> CalculateMD5Async(string fullFileName) 
{ 
    var block = ArrayPool<byte>.Shared.Rent(8192); 
    try 
    { 
    using (var md5 = MD5.Create()) 
    { 
     using (var stream = new FileStream(fullFileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8192, true)) 
     { 
      int length; 
      while ((length = await stream.ReadAsync(block, 0, block.Length).ConfigureAwait(false)) > 0) 
      { 
       md5.TransformBlock(block, 0, length, null, 0); 
      } 
      md5.TransformFinalBlock(block, 0, 0); 
     } 
     var hash = md5.Hash; 
     return Convert.ToBase64String(hash); 
     } 
    } 
    finally 
    { 
     ArrayPool<byte>.Shared.Return(block); 
    } 
} 
+0

Che cos'è 'ArrayPool'? – Shimmy

+0

OK capito: ['ArrayPool'] (https://github.com/dotnet/corefx/blob/master/src/System.Buffers/src/System/Buffers/ArrayPool.cs), è necessario installare il pacchetto [' System.Buffers'] (https://preview.nuget.org/packages/System.Buffers). – Shimmy

Problemi correlati