2010-01-23 9 views
31

Tôi cần tính tổng kiểm tra các tệp khá lớn (gigabyte). Điều này có thể được thực hiện bằng cách sử dụng phương pháp sau:Có thể tính giá trị băm MD5 (hoặc khác) với số lần đọc đệm không?

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

Tuy nhiên, các tệp này thường được viết ngay lập tức theo cách đệm (viết 32MB mỗi lần). Tôi rất thuyết phục rằng tôi đã thấy một sự ghi đè của hàm băm cho phép tôi tính giá trị băm MD5 (hoặc khác) cùng lúc bằng cách viết, tức là: tính toán giá trị băm của một bộ đệm, sau đó cho băm kết quả đó vào lần lặp tiếp theo .

Cái gì đó như thế này: (giả-ish)

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

băm bây giờ sililar những gì sẽ được thực hiện bằng cách chạy chức năng calcHash trên toàn bộ tập tin.

Bây giờ, tôi không thể tìm thấy bất kỳ phần ghi đè nào như vậy trong Khung 3.5., Tôi có đang mơ không? Nó chưa bao giờ tồn tại, hay tôi chỉ lousy khi tìm kiếm? Lý do để làm cả việc viết và kiểm tra tính toán cùng một lúc là bởi vì nó có ý nghĩa do các tệp lớn.

Trả lời

45

Bạn sử dụng các phương thức TransformBlockTransformFinalBlock để xử lý dữ liệu theo khối.

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

Lưu ý: Nó hoạt động (ít nhất là với các nhà cung cấp MD5) để gửi tất cả các khối để TransformBlock và sau đó gửi một khối trống để TransformFinalBlock để hoàn thành quá trình này.

+1

omg, chỉ cần đăng cùng một đề xuất, sử dụng cùng một định dạng =) –

+0

Ok, nhưng +1 cũng cung cấp tham chiếu! –

+1

Ay caramba! Nó đây rồi! Đó là chức năng tôi đang tìm kiếm. Thật tốt khi biết tôi đã không làm tất cả. Cảm ơn Guffa và Rubens đã cung cấp câu trả lời đúng như vậy ngay lập tức. 1 cho cả hai, tôi sẽ chấp nhận câu trả lời này vì ví dụ mã được bao gồm. –

3

thuật toán Hash được kỳ vọng sẽ giải quyết tình trạng này và thường được thực hiện với 3 chức năng:

hash_init() - Được gọi là phân bổ nguồn lực và bắt đầu quá trình băm.
hash_update() - Được gọi với dữ liệu mới khi nó đến.
hash_final() - Hoàn thành tính toán và tài nguyên miễn phí.

Nhìn vào http://www.openssl.org/docs/crypto/md5.html hoặc http://www.openssl.org/docs/crypto/sha.html để có ví dụ tiêu chuẩn tốt trong C; Tôi chắc rằng có những thư viện tương tự cho nền tảng của bạn.

+0

Câu trả lời hay, nhưng "where in it in ?net?" một phần của câu hỏi vẫn mở. –

+0

@Pascal: Xem 2 câu trả lời hay dưới đây, cả hai đều đã được đăng trước bình luận của bạn. –

4

Có vẻ bạn có thể sử dụng TransformBlock/TransformFinalBlock, như thể hiện trong mẫu này: Displaying progress updates when hashing large files

+0

Liên kết đó đã chết, hãy thử thay vào đó: http://www.infinitec.de/post/2007/06/09/Displaying-progress-updates-when-hashing-large-files.aspx – Cumbayah

48

Tôi thích câu trả lời ở trên, nhưng vì lợi ích của sự hoàn chỉnh, và là một giải pháp tổng quát hơn, hãy tham khảo các lớp CryptoStream. Nếu bạn đã xử lý luồng, bạn có thể dễ dàng quấn luồng của mình theo số CryptoStream, chuyển một thông số HashAlgorithm làm thông số 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)); 

Bạn có thể phải đóng dòng trước khi nhận được băm (vì vậy HashAlgorithm biết nó được thực hiện).

0

Tôi chỉ phải làm điều gì đó tương tự, nhưng muốn đọc tệp không đồng bộ. Nó đang sử dụng TransformBlock và TransformFinalBlock và cho tôi câu trả lời phù hợp với Azure, vì vậy tôi nghĩ nó là chính xác!

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

'ArrayPool' là gì? – Shimmy

+0

OK nhận được: ['ArrayPool'] (https://github.com/dotnet/corefx/blob/master/src/System.Buffers/src/System/Buffers/ArrayPool.cs), cần cài đặt gói [' System.Buffers'] (https://preview.nuget.org/packages/System.Buffers). – Shimmy