为输入流计算校验和

3

我需要计算输入流(或文件)的校验和,以检查文件内容是否更改。我有以下代码,尽管使用相同的流,但每次执行都会生成不同的值。有人可以帮我正确地完成吗?

public class CreateChecksum {
    public static void main(String args[]) {
        String test = "Hello world";
        ByteArrayInputStream bis = new ByteArrayInputStream(test.getBytes());
        System.out.println("MD5 checksum for file using Java : "    + checkSum(bis));
        System.out.println("MD5 checksum for file using Java : "    + checkSum(bis));
    }
    public static String checkSum(InputStream fis){
        String checksum = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            //Using MessageDigest update() method to provide input
            byte[] buffer = new byte[8192];
            int numOfBytesRead;
            while( (numOfBytesRead = fis.read(buffer)) > 0){
                md.update(buffer, 0, numOfBytesRead);
            }
            byte[] hash = md.digest();
            checksum = new BigInteger(1, hash).toString(16); //don't use this, truncates leading zero
        } catch (Exception ex) {                  
        }
       return checksum;
    }
}

1
这个问题具体是什么?为什么你要两次使用同一个流?你得到了d41d8cd98f00b204e9800998ecf8427e吗?在这种情况下,你可以重置它。 - Claude Martin
5个回答

5
你在两次调用中使用了同一个流对象 - 在你第一次调用checkSum之后,该流将不再有任何可读取的数据,因此第二次调用将会生成一个空流的哈希值。最简单的方法是每次创建一个新的流:
String test = "Hello world";
byte[] bytes = test.getBytes(StandardCharsets.UTF_8);
System.out.println("MD5 checksum for file using Java : " 
    + checkSum(new ByteArrayInputStream(bytes)));
System.out.println("MD5 checksum for file using Java : " 
    + checkSum(new ByteArrayInputStream(bytes)));

请注意,您在checkSum中的异常处理确实需要修复,以及您的十六进制转换...

0
你混淆了两个相关但不同的职责。
首先,你有一个提供读取内容的流。然后你对这个流进行了校验和;但是,你的实现是一个静态方法调用,有效地与一个类脱离关系,意味着没有人负责维护校验和。
尝试重新设计你的解决方案,如下所示:
public ChecksumInputStream implements InputStream {
  private InputStream in;

  public ChecksumInputStream(InputStream source) {
    this.in = source;
  }

  public int read() {
    int value = in.read();
    updateChecksum(value);
    return value;
  }

  // and repeat for all the other read methods.
}

请注意,现在您只需要进行一次读取,使用校验和计算器装饰原始输入流即可。

0

请查看org/apache/commons/codec/digest/DigestUtils.html中的代码


2
在这里提供相关的代码以及解释代码在哪里/如何帮助,或者提到 OP 的代码哪里出错了会更有帮助。 - Swapnil
你是对的,它甚至与任何其他代码都没有关系。这是一个bug。 - Mac Fang

0

对于文件的更改相对容易进行监控,File.lastModified()每次文件被更改(并关闭)时都会发生变化。甚至有一个内置API可用于通知所选文件系统更改:http://docs.oracle.com/javase/tutorial/essential/io/notification.html

InputStream的hashCode不适合用于检测更改(没有定义如何计算其hashCode - 很可能使用Object.hashCode,这意味着hashCode除了对象标识外不依赖于任何东西)。

像您尝试的那样构建类似MD5的东西可以工作,但需要每次读取整个文件。如果文件很大和/或要监视多个文件,则会严重影响性能。


0
问题出在第一次读取输入流后,pos已经到达了末尾。解决这个问题的快速方法是:

ByteArrayInputStream bis = new ByteArrayInputStream(test.getBytes()); System.out.println("使用Java计算文件的MD5校验和:" + checkSum(bis));

bis = new ByteArrayInputStream(test.getBytes());

    System.out.println("MD5 checksum for file using Java : "    + checkSum(bis));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接