2012-06-20 17 views
8

Ho un inputStream che voglio usare per calcolare un hash e salvare il file su disco. Mi piacerebbe sapere come farlo in modo efficiente. Devo utilizzare qualche attività per farlo contemporaneamente, dovrei duplicare lo stream passare a due flussi, uno per il metodo saveFile e uno per il metodo computeHash, o dovrei fare qualcos'altro?Calcolare l'hash durante il salvataggio di un file?

+1

ho fatto una domanda simile di recente: http://stackoverflow.com/questions/10985282/generate-running-hash-or-checksum-in-c (le risposte sono probabilmente applicabile qui a causa dei vincoli), ho assunto "hash" per indicare MD5, SHAx, ecc. –

+0

Ho usato SHA256Cng e posso anche salvare il file.La mia domanda è più di fare entrambe le cose contemporaneamente (usando task/future) o sequenzialmente (la lettura di un filestream sposta il puntatore interno, quindi posso resettare il puntatore a zero o duplicare il puntatore). Non so quale sia il migliore e come farlo. – Dave

+4

* riflette sulla lettura della domanda collegata * (Prendi in considerazione anche uno "stream splitter", che potrebbe essere usato per ridurre potenzialmente del lavoro manuale di copia tra due flussi di output.) –

risposta

0

È necessario inserire i byte del flusso in un byte[] per cancellarli.

+1

Puoi anche passare uno stream. Quali sarebbero i vantaggi della conversione del flusso in un byte []? – Dave

+0

Io, per qualche ragione, non ho visto quel sovraccarico. Mai. Io andrò a dire 10 "Hail Bills Gates" "in penance. – bluevector

+1

@Dave Non c'è alcun vantaggio. Sia la forma che accetta un 'byte []' che un 'Stream' stanno bloccando e si aspettano l'intero dato in un colpo solo. Con i thread e uno speciale 'Stream' ... ma questo aggiunge solo più problemi allora risolve ... –

3

Che dire di utilizzare un algoritmo di hash che funziona a livello di blocco? È possibile aggiungere il blocco all'hash (utilizzando TransformBlock) e successivamente scrivere il blocco sul blocco foreach del file nel flusso.

testato colpo di massima:

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

Non sono un grande fan dell'elaborazione manuale dei blocchi, ma dovrebbe funzionare. (Penso che CryptoStream sia un approccio più semplice che si riduce ad essere un bel wrapper.) –

+0

Concordato. Generalmente li evito come la peste (grazie a Dio per il recente metodo Stream.CopyTo) ... Penso che questo sia il modo migliore per risolvere il problema. Inoltre, una seconda lettura mi fa pensare che ho un bug in cui il blocco finale è stato sottoposto a hash due volte ... Per essere un MD5 accurato, dovresti rilevare la EOS e gestire l'ultimo blocco in modo diverso. –

1

potrebbe non essere l'opzione migliore, ma vorrei scegliere di andare per Stream discendente/involucro, quello che sarebbe pass-through per una realtà scrivere il file il disco.

Quindi:

  • derivare da Stream
  • dispone di un membro, come Stream _inner; che sarà il flusso di destinazione di scrivere
  • attuare Write() e tutte le cose relative
  • in Write() hash i blocchi di dati e chiamare _inner.Write()

Esempio di utilizzo

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

Ecco la mia soluzione, scrive una serie di struct (la variabile zecche) come file CSV (utilizzando il pacchetto di NuGet CsvHelper) e quindi crea un hash per scopi di checksum utilizzando il suffisso. sha256

Lo faccio scrivendo il csv su un memoryStream, quindi scrivendo il flusso di memoria su disco, quindi passando il memorystream all'algo hash.

Questa soluzione consente di mantenere l'intero file come un memorandum. Va bene per tutto tranne i file multi-gigabyte che ti farebbero uscire dalla ram. Se dovessi farlo di nuovo, probabilmente tenterei di usare l'approccio CryptoStream, ma questo è abbastanza buono per i miei scopi prevedibili.

Ho verificato tramite uno strumento di terze parti che gli hash sono validi.

Ecco il codice:

//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

Questo metodo copia e hash con i flussi concatenati.

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

campione completa vedere https://gist.github.com/dhcgn/da1637277d9456db9523a96a0a34da78

Problemi correlati