2010-01-23 19 views
31

Necesito calcular sumas de comprobación de archivos bastante grandes (gigabytes). Esto se puede lograr mediante el siguiente método:¿Es posible calcular hash MD5 (u otro) con lecturas almacenadas?

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; 
    } 

Sin embargo, los archivos se escriben normalmente acaba de antelación y de manera tamponada (por ejemplo la escritura de 32MB a la vez). Estoy tan convencido de que vi una anulación de una función hash que me permitió calcular un hash MD5 (u otro) al mismo tiempo que la escritura, es decir: calcular el hash de un buffer, y luego alimentar el hash resultante en la siguiente iteración .

Algo como esto: (pseudo-ish)

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

hash es ahora sililar con lo que se logra mediante la ejecución de la función calcHash sobre el archivo completo.

Ahora, no puedo encontrar ninguna anulación así en .Net Framework 3.5, ¿estoy soñando? ¿Nunca existió, o soy pésimo en la búsqueda? El motivo por el que se realizan cálculos tanto de escritura como de suma de comprobación de una vez es porque tiene sentido debido a los archivos de gran tamaño. Se espera que

Respuesta

45

Utiliza los métodos TransformBlock y TransformFinalBlock para procesar los datos en fragmentos.

// 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: Funciona (al menos con el proveedor MD5) para enviar todos los bloques a TransformBlock y luego enviar un bloque vacío a TransformFinalBlock para finalizar el proceso.

+1

omg, acaba de publicar la misma sugerencia, con el mismo formato =) –

+0

Ok, pero +1 también proporciona una referencia! –

+1

Ay caramba! ¡Ahí está! Esa era la función que estaba buscando. Es bueno saber que no estaba inventando todo. Gracias a Guffa y Rubens por proporcionar la respuesta correcta tan rápidamente. +1 a ambos, aceptaré esta respuesta debido al ejemplo de código incluido. –

3

algoritmos hash para manejar esta situación y se implementan típicamente con 3 funciones:

hash_init() - llamada para asignar recursos y comenzar el hash.
hash_update() - Se invoca con nuevos datos cuando llega.
hash_final() - Complete el cálculo y los recursos gratuitos.

Mire http://www.openssl.org/docs/crypto/md5.html o http://www.openssl.org/docs/crypto/sha.html para buenos, ejemplos estándar en C; Estoy seguro de que hay bibliotecas similares para su plataforma.

+0

Buena respuesta, pero el "¿dónde está en .net?" parte de la pregunta permanece abierta –

+0

@Pascal: Vea las 2 buenas respuestas a continuación, que se han publicado antes de su comentario. –

4

parece que se puede utilizar TransformBlock/TransformFinalBlock, como se muestra en este ejemplo: Displaying progress updates when hashing large files

+0

Ese enlace está muerto, intente esto en su lugar: http://www.infinitec.de/post/2007/06/09/Displaying-progress-updates-when-hashing-large-files.aspx – Cumbayah

48

me gusta la respuesta anterior, pero en aras de la exhaustividad, y ser una solución más general, referirse a la clase CryptoStream. Si ya está manejando las transmisiones, es fácil ajustar la transmisión en un CryptoStream, pasando un HashAlgorithm como el parámetro 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)); 

Es posible que tenga que cerrar la corriente antes de obtener el hash (por lo que el HashAlgorithm sabe que ha hecho).

0

He tenido que hacer algo similar, pero quería leer el archivo de forma asincrónica. Está usando TransformBlock y TransformFinalBlock y me está dando respuestas consistentes con Azure, ¡así que creo que es correcto!

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

¿Qué es 'ArrayPool'? – Shimmy

+0

OK lo consiguió: ['ArrayPool'] (https://github.com/dotnet/corefx/blob/master/src/System.Buffers/src/System/Buffers/ArrayPool.cs), necesita instalar el paquete [' System.Buffers'] (https://preview.nuget.org/packages/System.Buffers). – Shimmy