如何使用java或groovy计算目录上的md5校验和?

时间:2010-06-09 21:25:58

标签: java groovy directory md5 checksum

我希望使用java或groovy来获取完整目录的md5校验和。

我必须将源目录,目标,校验和源和目标以及删除源目录后的目录复制。

我找到了这个文件的脚本,但是如何用目录做同样的事情呢?

import java.security.MessageDigest

def generateMD5(final file) {
    MessageDigest digest = MessageDigest.getInstance("MD5")
    file.withInputStream(){ is ->
        byte[] buffer = new byte[8192]
        int read = 0
        while( (read = is.read(buffer)) > 0) {
            digest.update(buffer, 0, read);
        }
    }
    byte[] md5sum = digest.digest()
    BigInteger bigInt = new BigInteger(1, md5sum)

    return bigInt.toString(16).padLeft(32, '0')
}

有更好的方法吗?

7 个答案:

答案 0 :(得分:9)

我有相同的要求,并选择我的'目录哈希'作为目录中所有(非目录)文件的连接流的MD5哈希。正如crozin在a similar question的评论中提到的那样,您可以使用SequenceInputStream作为连接其他流的负载的流。我正在使用Apache Commons Codec作为MD5算法。

基本上,您通过目录树递归,将FileInputStream个实例添加到Vector非目录文件。 Vector然后方便地使用elements()方法提供Enumeration需要循环的SequenceInputStream。对于MD5算法,它只显示为一个InputStream

一个问题是,每次哈希与相同输入相同时,您需要以相同顺序显示的文件。 listFiles()中的File方法不保证排序,因此我按文件名排序。

我这样做是为SVN控制的文件,并希望避免散列隐藏的SVN文件,所以我实现了一个标志以避免隐藏文件。

相关基本代码如下。 (显然它可以'硬化'。)

import org.apache.commons.codec.digest.DigestUtils;

import java.io.*;
import java.util.*;

public String calcMD5HashForDir(File dirToHash, boolean includeHiddenFiles) {

    assert (dirToHash.isDirectory());
    Vector<FileInputStream> fileStreams = new Vector<FileInputStream>();

    System.out.println("Found files for hashing:");
    collectInputStreams(dirToHash, fileStreams, includeHiddenFiles);

    SequenceInputStream seqStream = 
            new SequenceInputStream(fileStreams.elements());

    try {
        String md5Hash = DigestUtils.md5Hex(seqStream);
        seqStream.close();
        return md5Hash;
    }
    catch (IOException e) {
        throw new RuntimeException("Error reading files to hash in "
                                   + dirToHash.getAbsolutePath(), e);
    }

}

private void collectInputStreams(File dir,
                                 List<FileInputStream> foundStreams,
                                 boolean includeHiddenFiles) {

    File[] fileList = dir.listFiles();        
    Arrays.sort(fileList,               // Need in reproducible order
                new Comparator<File>() {
                    public int compare(File f1, File f2) {                       
                        return f1.getName().compareTo(f2.getName());
                    }
                });

    for (File f : fileList) {
        if (!includeHiddenFiles && f.getName().startsWith(".")) {
            // Skip it
        }
        else if (f.isDirectory()) {
            collectInputStreams(f, foundStreams, includeHiddenFiles);
        }
        else {
            try {
                System.out.println("\t" + f.getAbsolutePath());
                foundStreams.add(new FileInputStream(f));
            }
            catch (FileNotFoundException e) {
                throw new AssertionError(e.getMessage()
                            + ": file should never not be found!");
            }
        }
    }

}

答案 1 :(得分:4)

我做了一个函数来计算目录上的MD5校验和:

首先,我正在使用FastMD5:http://www.twmacinta.com/myjava/fast_md5.php

这是我的代码:

  def MD5HashDirectory(String fileDir) {
    MD5 md5 = new MD5();
    new File(fileDir).eachFileRecurse{ file ->
      if (file.isFile()) {
        String hashFile = MD5.asHex(MD5.getHash(new File(file.path)));
        md5.Update(hashFile, null);
      }

    }
    String hashFolder = md5.asHex();
    return hashFolder
  }

答案 2 :(得分:2)

HashCopy是一个Java应用程序。它可以递归地生成和验证单个文件或目录上的MD5和SHA。我不确定它是否有API。它可以从www.jdxsoftware.org下载。

答案 3 :(得分:2)

根据Stuart Rossiter的答案,但干净的代码和隐藏的文件得到妥善处理:

import org.apache.commons.codec.digest.DigestUtils;

import java.io.*;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Vector;

public class Hashing
{
    public static String hashDirectory(String directoryPath, boolean includeHiddenFiles) throws IOException
    {
        File directory = new File(directoryPath);

        if (!directory.isDirectory())
        {
            throw new IllegalArgumentException("Not a directory");
        }

        Vector<FileInputStream> fileStreams = new Vector<>();
        collectFiles(directory, fileStreams, includeHiddenFiles);

        try (SequenceInputStream sequenceInputStream = new SequenceInputStream(fileStreams.elements()))
        {
            return DigestUtils.md5Hex(sequenceInputStream);
        }
    }

    private static void collectFiles(File directory,
                                     List<FileInputStream> fileInputStreams,
                                     boolean includeHiddenFiles) throws IOException
    {
        File[] files = directory.listFiles();

        if (files != null)
        {
            Arrays.sort(files, Comparator.comparing(File::getName));

            for (File file : files)
            {
                if (includeHiddenFiles || !Files.isHidden(file.toPath()))
                {
                    if (file.isDirectory())
                    {
                        collectFiles(file, fileInputStreams, includeHiddenFiles);
                    } else
                    {
                        fileInputStreams.add(new FileInputStream(file));
                    }
                }
            }
        }
    }
}

答案 4 :(得分:0)

目前尚不清楚采用目录的md5sum意味着什么。您可能需要文件列表的校验和;您可能需要文件列表及其内容的校验和。如果您已经自己对文件数据进行求和,我建议您为目录列表指定一个明确的表示形式(注意文件名中的恶意字符),然后每次计算和散列。您还需要考虑如何处理特殊文件(unix世界中的套接字,管道,设备和符号链接; NTFS有文件流,我相信类似于符号链接)。

答案 5 :(得分:0)

我计算了sha512而不是md5(因为它更安全),但是您可以在gradle文件或原始groovy中定义它。

import java.security.MessageDigest
import java.io.File

def calcDirHash(fileDir) {
  def hash = MessageDigest.getInstance("SHA-512")
  new File(fileDir).eachFileRecurse{ file ->
    if (file.isFile()) {
      file.eachByte 4096, {bytes, size ->
        hash.update(bytes, 0, size);
      }
    }
  }
  return hash.digest().encodeHex()
}

然后在任何任务中调用calcDirHash(并传递要散列的目录)。

您可以使用其他编码方案代替SHA-512。

答案 6 :(得分:0)

如果您需要在Gradle构建文件中执行此操作,则比使用普通Groovy简单得多。

这是一个例子:

def sources = fileTree('rootDir').matching {
    include 'src/*', 'build.gradle'
}.sort { it.name }
def digest = MessageDigest.getInstance('SHA-1')
sources.each { digest.update(it.bytes) }
digest.digest().encodeHex().toString()

MessageDigest来自Java std库:https://docs.oracle.com/javase/8/docs/api/java/security/MessageDigest.html

所有JVM支持的算法是:

MD5
SHA-1
SHA-256