2012-06-20 21 views
8

Tengo un inputStream que quiero usar para calcular un hash y guardar el archivo en el disco. Me gustaría saber cómo hacerlo de manera eficiente. ¿Debo usar alguna tarea para hacer eso al mismo tiempo, debería duplicar el pase de transmisión a dos flujos, uno para el método saveFile y otro para el método computeHash, o debería hacer algo más?¿Computing Hash al guardar un archivo?

+1

me hizo una pregunta similar hace poco: http://stackoverflow.com/questions/10985282/generate-running-hash-or-checksum-in-c (las respuestas son probablemente aplicable aquí debido a las restricciones), asumí que "hash" significaba MD5, SHAx, etc. –

+0

He usado SHA256Cng y también puedo guardar el archivo.Mi pregunta es más sobre hacer ambas cosas al mismo tiempo (usando tareas/futuros) o secuencialmente (leer una secuencia de archivos mueve el puntero interno, por lo que puedo restablecer el puntero a cero o duplicar el puntero). No sé cuál es mejor y cómo hacerlo. – Dave

+4

* reflexiona sobre leer la pregunta vinculada * (También considere un "divisor de flujo", que podría usarse para reducir potencialmente algunos trabajos manuales de copia entre dos flujos de salida). –

Respuesta

0

Tendrá que rellenar los bytes de la secuencia en un byte[] para picarlos.

+1

También puede pasar una transmisión. ¿Cuáles serían los beneficios de convertir el flujo a un byte []? – Dave

+0

Yo, por alguna razón, no vi esa sobrecarga. Nunca. Iré a decir 10 "Hail Bills Gates" en penitencia. – bluevector

+1

@Dave No hay ventaja. Tanto el formulario que toma un 'byte []' como un 'Stream' están bloqueando y esperan los datos completos en one-shot. Con hilos y un 'Stream' especial ... pero eso solo agrega más problemas, entonces resuelve ... –

3

¿Qué pasa con el uso de algoritmos hash que operan en un nivel de bloque? Puede agregar el bloque al hash (usando TransformBlock) y posteriormente escribir el bloque en el bloque foreach del archivo en la secuencia.

No comprobado tiro áspera:

using System.IO; 
using System.Security.Cryptography; 

... 

public byte[] HashedFileWrite(string filename, Stream input) 
{ 
    var hash_algorithm = MD5.Create(); 

    using(var file = File.OpenWrite(filename)) 
    { 
     byte[] buffer = new byte[4096]; 
     int read = 0; 

     while ((read = input.Read(buffer, 0, buffer.Length)) > 0) 
     { 
      hash_algorithm.TransformBlock(buffer, 0, read, null, 0); 
      file.Write(buffer, 0, read); 
     } 

     hash_algorithm.TransformFinalBlock(buffer, 0, read); 
    } 

    return hash_algorithm.Hash; 
} 
+0

No soy un gran admirador del procesamiento manual de bloques, pero debería funcionar. (Creo que el CryptoStream es un enfoque más simple que se reduce a ser un envoltorio bonito.) –

+0

De acuerdo. Generalmente los evito como la peste (gracias a Dios por el reciente método Stream.CopyTo) ... Creo que esta es la mejor manera de resolver el problema. Además, una segunda lectura me hace pensar que tengo un error donde el bloque final es hash dos veces ... Para ser un MD5 preciso, tendrías que detectar el EOS y manejar el último bloque de forma diferente. –

1

tal vez no sea la mejor opción, pero me gustaría optar por ir a Stream descendiente/envoltura, el que sería el paso a través de una realidad escribir el archivo en el disco.

Así:

  • derivar de Stream
  • tienen uno de los miembros, como Stream _inner; que será el flujo de destino para escribir
  • implemento Write() y todas las cosas relacionadas con
  • en Write() hash de los bloques de datos y llamada _inner.Write()

Ejemplo de uso

Stream s = File.Open("infile.dat"); 
Stream out = File.Create("outfile.dat"); 
HashWrapStream hasher = new HashWrapStream(out); 
byte[] buffer=new byte[1024]; 
int read = 0; 
while ((read=s.Read(buffer)!=0) 
{ 
    hasher.Write(buffer); 
} 
long hash=hasher.GetComputedHash(); // get actual hash 
hasher.Dispose(); 
s.Dispose(); 
0

Aquí está mi solución, escribe una serie de estructuras (la variable garrapatas) como un archivo CSV (utilizando el paquete Nuget CsvHelper) y luego crea un hash para fines de suma de control mediante el sufijo. sha256

Lo hago escribiendo el csv en un memoryStream, luego escribiendo el flujo de memoria en el disco, y pasando el flujo de memoria al hash algo.

Esta solución mantiene todo el archivo como un flujo de memoria. Está bien para todo excepto para archivos de varios gigabytes que te sacarían de ram. Si tuviera que hacer esto de nuevo, probablemente intente usar el enfoque CryptoStream, pero esto es lo suficientemente bueno para mis propósitos previsibles.

He verificado a través de una herramienta de terceros que los valores hash son válidos.

Aquí está el código:

//var ticks = **some_array_you_want_to_write_as_csv** 

using (var memoryStream = new System.IO.MemoryStream()) 
      { 
       using (var textWriter = new System.IO.StreamWriter(memoryStream)) 
       { 
        using (var csv = new CsvHelper.CsvWriter(textWriter)) 
        { 
         csv.Configuration.DetectColumnCountChanges = true; //error checking 
         csv.Configuration.RegisterClassMap<TickDataClassMap>(); 
         csv.WriteRecords(ticks); 

         textWriter.Flush(); 

         //write to disk 
         using (var fileStream = new System.IO.FileStream(targetFileName, System.IO.FileMode.Create)) 
         { 
          memoryStream.Position = 0; 
          memoryStream.CopyTo(fileStream); 

         } 

         //write sha256 hash, ensuring that the file was properly written 
         using (var sha256 = System.Security.Cryptography.SHA256.Create()) 
         { 
          memoryStream.Position = 0; 
          var hash = sha256.ComputeHash(memoryStream); 
          using (var reader = System.IO.File.OpenRead(targetFileName)) 
          { 
           System.IO.File.WriteAllText(targetFileName + ".sha256", hash.ConvertByteArrayToHexString()); 
          } 
         } 

        } 

       } 
      } 
2

Este método copiar y hash con corrientes encadenadas.

private static byte[] CopyAndHash(string source, string target, Action<double> progress, Func<bool> isCanceled) 
{ 
    using(var sha512 = SHA512.Create()) 
    using (var targetStream = File.OpenWrite(target)) 
    using (var cryptoStream = new CryptoStream(targetStream, sha512, CryptoStreamMode.Write)) 
    using (var sourceStream = File.OpenRead(source)) 
    { 
     byte[] buffer = new byte[81920]; 
     int read; 
     while ((read = sourceStream.Read(buffer, 0, buffer.Length)) > 0 && !isCanceled()) 
     { 
      cryptoStream.Write(buffer, 0, read); 

      progress?.Invoke((double) sourceStream.Length/sourceStream.Position * 100); 
     } 

    File.SetAttributes(target, File.GetAttributes(source)); 

    return sha512.Hash; 
    } 
} 

muestra completa ver https://gist.github.com/dhcgn/da1637277d9456db9523a96a0a34da78

Cuestiones relacionadas