2012-06-20 19 views
8

Mam inputStream, którego chcę użyć do obliczenia hasza i zapisać plik na dysku. Chciałbym wiedzieć, jak to zrobić skutecznie. Czy powinienem użyć jakiegoś zadania, aby wykonać to jednocześnie, czy powinienem duplikować strumień przejścia do dwóch strumieni, jeden dla metody saveFile i jeden dla metody computeHash, czy powinienem zrobić coś innego?Computing Hash podczas zapisywania pliku?

+1

zadałem podobne pytanie niedawno: http://stackoverflow.com/questions/10985282/generate-running-hash-or-checksum-in-c (odpowiedzi są prawdopodobnie ma to zastosowanie ze względu na ograniczenia), założyłem "hash" na oznaczenie MD5, SHAx itd. –

+0

Użyłem SHA256Cng i mogę również zapisać plik.Moje pytanie dotyczy raczej robienia obu w tym samym czasie (przy użyciu zadań/kontraktów futures) lub sekwencyjnie (odczyt strumienia filaru przesuwa wskaźnik wewnętrzny, więc mogę zresetować wskaźnik do zera lub zduplikować wskaźnik). Nie wiem, który z nich jest lepszy i jak to zrobić. – Dave

+4

* Rozmyśla o przeczytaniu połączonego pytania * (Rozważ także "rozdzielacz strumienia", który mógłby zostać użyty do potencjalnego zmniejszenia ręcznej pracy kopiowania pomiędzy dwoma strumieniami wyjściowymi.) –

Odpowiedz

0

Musisz rzeczy bajtów strumienia jest w byte[] w celu ich mieszania.

+1

Możesz także przekazać strumień. Jakie byłyby korzyści konwersji strumienia na bajt []? – Dave

+0

Ja, z jakiegoś powodu, nie zauważyłem przeciążenia. Zawsze. Pojadę 10 razy "Hail Bills Gates" "w pokucie. – bluevector

+1

@Dave Nie ma żadnej korzyści. Obie formy, które pobierają "byte []" i "Stream", blokują i oczekują wszystkich danych w jednym ujęciu. Z wątkami i specjalnym 'Streamem ... ... ale to tylko dodaje więcej problemów, to rozwiązuje ... –

3

A co z wykorzystaniem algorytmów mieszających działających na poziomie bloku? Możesz dodać blok do hasha (używając TransformBlock), a następnie zapisać blok do bloku foreach pliku w strumieniu.

Nietestowane szorstki strzał:

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

Nie jestem wielkim fanem ręcznego przetwarzania bloków, ale to powinno działać. (Myślę, że CryptoStream jest prostszym podejściem, które sprowadza się do bycia ładnym opakowaniem.) –

+0

Zgoda. Generalnie unikam ich jak zarazy (dzięki Bogu za najnowszą metodę Stream.CopyTo) ... Myślę, że jest to najlepszy sposób na rozwiązanie problemu. Również drugi odczyt sprawia, że ​​myślę, że mam błąd, w którym ostatni blok został zakodowany dwukrotnie ... Aby być dokładnym MD5, musiałbyś wykryć EOS i obsługiwać ostatni blok w inny sposób. –

1

To może nie być najlepszym rozwiązaniem, ale wybrałbym iść do Stream potomka/owijki, ten, który byłby pass-through dla jednej rzeczywiście pisanie plik dysk.

Więc:

  • pochodzą z Stream
  • mieć jeden członek takich jak Stream _inner; który będzie strumień cel napisania
  • wdrożyć Write() i wszystkie rzeczy związane
  • w Write() hash bloków dane i zadzwoń pod numer _inner.Write()

Przykład zastosowania

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

jest mój rozwiązanie zapisuje szereg elemencie (zmienna kleszcze) jako csv (przy użyciu zestawu Nuget CsvHelper), a następnie tworzy mieszania dla celów sumy kontrolnej z wykorzystaniem sufiksu. sha256

Robię to, pisząc csv do memoryStream, a następnie zapisując strumień pamięci na dysk, a następnie przesyłając memorystream do skrótu algo.

Dzięki temu rozwiązaniu cały plik jest przechowywany w pamięci memorystycznej. Wszystko oprócz plików wielogigabajtowych, które wyłączyłyby Cię z pamięci RAM, jest w porządku. Gdybym musiał to zrobić ponownie, prawdopodobnie spróbowałbym użyć podejścia CryptoStream, ale jest to wystarczająco dobre dla moich przewidywalnych celów.

Sprawdziłem za pomocą narzędzia innej firmy, że skróty są ważne.

Oto kod:

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

Ta metoda kopiowania i hash W powiązanych strumieni.

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

Pełna próbka zobaczyć https://gist.github.com/dhcgn/da1637277d9456db9523a96a0a34da78

Powiązane problemy