How do you create the hash of a folder in C#?

Viewed 30739

I need to create the hash for a folder that contains some files. I've already done this task for each of the files, but I'm searching for a way to create one hash for all files in a folder. Any ideas on how to do that?

(Of course I can create the hash for each file and concatenate it to some big hash but it's not a way I like)

7 Answers

Here's a solution that uses streaming to avoid memory and latency issues.

By default the file paths are included in the hashing, which will factor not only the data in the files, but the file system entries themselves, which avoids hash collisions. This post is tagged security, so this ought to be important.

Finally, this solution puts you in control of the hashing algorithm, which files get hashed, and in what order.

public static class HashAlgorithmExtensions
{
    public static async Task<byte[]> ComputeHashAsync(this HashAlgorithm alg, IEnumerable<FileInfo> files, bool includePaths = true)
    {
        using (var cs = new CryptoStream(Stream.Null, alg, CryptoStreamMode.Write))
        {
            foreach (var file in files)
            {
                if (includePaths)
                {
                    var pathBytes = Encoding.UTF8.GetBytes(file.FullName);
                    cs.Write(pathBytes, 0, pathBytes.Length);
                }

                using (var fs = file.OpenRead())
                    await fs.CopyToAsync(cs);
            }

            cs.FlushFinalBlock();
        }

        return alg.Hash;
    }
}

An example that hashes all the files in a folder:

async Task<byte[]> HashFolder(DirectoryInfo folder, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly)
{
    using(var alg = MD5.Create())
        return await alg.ComputeHashAsync(folder.EnumerateFiles(searchPattern, searchOption));
}

Quick and Dirty folder hash that does not go down to suborders or read binary data. It is based on file and sub-folder names.

Public Function GetFolderHash(ByVal sFolder As String) As String
    Dim oFiles As List(Of String) = IO.Directory.GetFiles(sFolder).OrderBy(Function(x) x.Count).ToList()
    Dim oFolders As List(Of String) = IO.Directory.GetDirectories(sFolder).OrderBy(Function(x) x.Count).ToList()
    oFiles.AddRange(oFolders)

    If oFiles.Count = 0 Then
        Return ""
    End If

    Dim oDM5 As System.Security.Cryptography.MD5 = System.Security.Cryptography.MD5.Create()
    For i As Integer = 0 To oFiles.Count - 1
        Dim sFile As String = oFiles(i)
        Dim sRelativePath As String = sFile.Substring(sFolder.Length + 1)
        Dim oPathBytes As Byte() = System.Text.Encoding.UTF8.GetBytes(sRelativePath.ToLower())

        If i = oFiles.Count - 1 Then
            oDM5.TransformFinalBlock(oPathBytes, 0, oPathBytes.Length)
        Else
            oDM5.TransformBlock(oPathBytes, 0, oPathBytes.Length, oPathBytes, 0)
        End If
    Next

    Return BitConverter.ToString(oDM5.Hash).Replace("-", "").ToLower()
End Function
Related