2012-06-20 21 views
8

Tôi có một số inputStream mà tôi muốn sử dụng để tính toán băm và lưu tệp vào đĩa. Tôi muốn biết làm thế nào để làm điều đó một cách hiệu quả. Tôi có nên sử dụng một số tác vụ để thực hiện đồng thời không, tôi có nên sao chép luồng qua hai luồng, một cho phương thức saveFile và một cho phương thức computeHash hay tôi nên làm gì khác?Tính toán Hash trong khi lưu một tệp?

+1

Tôi hỏi một câu hỏi tương tự gần đây: http://stackoverflow.com/questions/10985282/generate-running-hash-or-checksum-in-c (câu trả lời là có thể áp dụng ở đây do các ràng buộc), tôi giả định "băm" có nghĩa là MD5, SHAx, v.v. –

+0

Tôi đã sử dụng SHA256Cng và cũng có thể lưu tệp.Câu hỏi của tôi là nhiều hơn về làm cả hai cùng một lúc (sử dụng nhiệm vụ/tương lai) hoặc tuần tự (đọc một filestream di chuyển con trỏ nội bộ, vì vậy tôi có thể thiết lập lại con trỏ về số không hoặc nhân đôi con trỏ). Tôi không biết cái nào tốt hơn và cách làm. – Dave

+4

* suy ngẫm về việc đọc câu hỏi được liên kết * (Cũng xem xét một "bộ tách luồng", có thể được sử dụng để giảm khả năng sao chép thủ công giữa hai luồng đầu ra.) –

Trả lời

0

Bạn sẽ cần xếp các byte của luồng vào một số byte[] để băm chúng.

+1

Bạn cũng có thể truyền luồng. Lợi ích của việc chuyển đổi luồng thành byte [] là gì? – Dave

+0

Tôi, vì một lý do nào đó, không thấy tình trạng quá tải đó. Không bao giờ. Tôi sẽ đi nói 10 "Hail Bills Gates" trong sự trừng phạt. – bluevector

+1

@Dave Không có lợi thế. Cả hai hình thức mà có một 'byte []' và một 'Stream' đang chặn và mong đợi toàn bộ dữ liệu trong một shot. Với chủ đề và một 'Stream' đặc biệt ... nhưng điều đó chỉ thêm nhiều vấn đề hơn sau đó nó giải quyết ... –

3

Điều gì về việc sử dụng thuật toán băm hoạt động ở cấp khối? Bạn có thể thêm khối vào băm (sử dụng TransformBlock) và sau đó ghi khối đó vào khối foreach trong luồng.

chưa được kiểm tra bắn thô:

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

Tôi không phải là một fan hâm mộ lớn của việc xử lý khối thủ công, nhưng điều này nên làm việc. (Tôi nghĩ rằng CryptoStream là một cách tiếp cận đơn giản mà đi xuống để được một wrapper đẹp.) –

+0

Đồng ý. Tôi thường tránh chúng như bệnh dịch hạch (Cảm ơn Chúa vì phương thức Stream.CopyTo gần đây) ... Tôi nghĩ đây là cách tốt nhất để giải quyết vấn đề tho. Ngoài ra, một lần đọc thứ hai khiến tôi nghĩ rằng tôi có một lỗi trong đó khối cuối cùng được băm hai lần ... Để trở thành MD5 chính xác, bạn sẽ phải phát hiện EOS và xử lý khối cuối cùng khác nhau. –

1

Nó có thể không phải là lựa chọn tốt nhất, nhưng tôi sẽ lựa chọn để đi cho Stream hậu duệ/wrapper, một trong đó sẽ được pass-through cho ai thực sự viết hồ sơ cho Cái đĩa.

Vì vậy:

  • xuất phát từ Stream
  • có một thành viên như Stream _inner; đó sẽ là dòng mục tiêu để viết
  • thực hiện Write() và tất cả những thứ liên quan
  • trong Write() băm các khối dữ liệu và gọi _inner.Write()

Cách sử dụng ví dụ

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

Đây là giải pháp của tôi, nó viết một mảng của cấu trúc (biến ve) như một tập tin csv (sử dụng gói NuGet CsvHelper) và sau đó tạo ra một hash cho mục đích kiểm tra bằng cách sử dụng hậu tố. sha256

Tôi làm điều này bằng cách viết csv vào memoryStream, sau đó ghi luồng bộ nhớ vào đĩa, sau đó chuyển bộ nhớ tới bản băm.

Giải pháp này đang giữ toàn bộ tệp ở dạng bộ nhớ. Nó là tốt cho tất cả mọi thứ ngoại trừ các tập tin đa gigabyte mà sẽ chạy bạn ra khỏi ram. Nếu tôi phải làm điều này một lần nữa, tôi có thể thử sử dụng phương pháp CryptoStream, nhưng điều này là đủ tốt cho các mục đích dự đoán của tôi.

Tôi đã xác minh qua công cụ của bên thứ ba mà các băm hợp lệ.

Đây là mã:

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

Phương pháp này sẽ sao chép và băm với suối xích.

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

Full mẫu thấy https://gist.github.com/dhcgn/da1637277d9456db9523a96a0a34da78

Các vấn đề liên quan