stop hashing operation using filestream

2019-08-01 01:42发布

问题:

I'm having this piece of code that computes the MD5 hash for a given input file.

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

I'm running this time consuming operation in a separate thread. For very big files this operation may take some seconds/minutes. What I want to do is to be able to stop the operation from another thread, for example using a "Stop" button in the GUI. Any suggestions?

回答1:

You can read file parts and apply MD5.TransformBlock to each read part. (Notice, that last part should be read with MD5.TransformFinalBlock). Between processing each block you can check if cancellation is required, you are free to use any synchronization primitives you like.

Here is example, that uses 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;
            }
        }
    }
}