使用文件流停止哈希操作

stop hashing operation using filestream

我正在使用这段代码计算给定输入文件的 MD5 哈希值。

    public static String ComputeMD5(String filename)
    {
        using (var md5 = MD5.Create())
        {
            try
            {
                using (var stream = File.OpenRead(filename))
                {
                    return BitConverter.ToString(md5.ComputeHash(stream)).Replace("-", "").ToLower();
                }
            }
            catch (Exception)
            {
                // File is not accessible, return String.Empty
                return String.Empty;
            }
        }
    }

我运行这个耗时的操作在一个单独的线程中。对于非常大的文件,此操作可能需要 seconds/minutes。我想要做的是能够从另一个线程停止操作,例如在 GUI 中使用 "Stop" 按钮。有什么建议吗?

您可以阅读文件部分并应用 MD5.TransformBlock to each read part. (Notice, that last part should be read with MD5.TransformFinalBlock)。 在处理每个块之间,您可以检查是否需要取消,您可以自由使用任何您喜欢的同步原语。

这里是例子,使用 CancellationToken:

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

namespace Stack
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var cancellationTokenSource = new CancellationTokenSource())
            {
                var thread = new Thread(() =>
                {
                    try
                    {
                        var hash = CalcHash("D:/Image.iso", cancellationTokenSource.Token);
                        Console.WriteLine($"Done: hash is {BitConverter.ToString(hash)}");
                    }
                    catch (OperationCanceledException)
                    {
                        Console.WriteLine("Canceled :(");
                    }
                });
                // Start background thread
                thread.Start();

                Console.WriteLine("Working, press any key to exit");
                Console.ReadLine();
                cancellationTokenSource.Cancel();
            }

            Console.WriteLine("Finished");
            Console.ReadLine();
        }

        static byte[] CalcHash(string path, CancellationToken ct)
        {
            using (var stream = File.OpenRead(path))
            using (var md5 = MD5.Create())
            {
                const int blockSize = 1024 * 1024 * 4;
                var buffer = new byte[blockSize];
                long offset = 0;

                while (true)
                {
                    ct.ThrowIfCancellationRequested();
                    var read = stream.Read(buffer, 0, blockSize);
                    if (stream.Position == stream.Length)
                    {
                        md5.TransformFinalBlock(buffer, 0, read);
                        break;
                    }
                    offset += md5.TransformBlock(buffer, 0, buffer.Length, buffer, 0);
                    Console.WriteLine($"Processed {offset * 1.0 / 1024 / 1024} MB so far");
                }

                return md5.Hash;
            }
        }
    }
}