Java InputStream:同时复制并计算哈希

Jor*_*rdi 3 java java-io java-nio

这是我的两个代码片段:

public class Uploader {

  private static final String SHA_256 = "SHA-256";

  public String getFileSHA2Checksum(InputStream fis) throws IOException {
    try {
      MessageDigest md5Digest = MessageDigest.getInstance(SHA_256);
      return getFileChecksum(md5Digest, fis);
    } catch (NoSuchAlgorithmException e) {
      return "KO";
    }
  }

  public void transferTo(InputStream fis) throws IOException {
    FileUtils.copyInputStreamToFile(fis, file2);
  }
Run Code Online (Sandbox Code Playgroud)

我的代码使用此类:

是否有可能copyToFile同时calculateChecksum杠杆InputStream开放?

Mar*_*eel 7

您可以DigestInputStream在从流读取时使用 来计算哈希值。也就是说,您用 a 包装原始输入流DigestInputStream并读取DigestInputStream. 读取数据时,消息摘要会自动更新,您可以在读取整个流后检索摘要。

或者,您可以DigestOutputStream在写入流时计算哈希值。以类似的方式,您可以用 a 包装目标输出流DigestOutputStream并通过DigestOutputStream.

一个快速而肮脏的例子:

var inputFile = Path.of("D:\\Development\\data\\testdata-csv\\customers-1000.csv");
var outputFile = Files.createTempFile("testoutput", ".dat");
var md = MessageDigest.getInstance("SHA-256");
try (var in = new DigestInputStream(Files.newInputStream(inputFile), md);
     var out = Files.newOutputStream(outputFile)) {
    in.transferTo(out);
} finally {
    Files.deleteIfExists(outputFile);
}

System.out.println(HexFormat.of().formatHex(md.digest()));
Run Code Online (Sandbox Code Playgroud)

就您现有的代码而言,您可以执行以下操作:

public String transferAndHash(InputStream in) throws IOException {
    try {
        var md = MessageDigest.getInstance("SHA-256");
        try (var digestIn = new DigestInputStream(in, md)) {
            transferTo(digestIn);
        }
        return HexFormat.of().formatHex(md.digest());
    } catch (NoSuchAlgorithmException e) {
        // all recent Java versions are required to support SHA-256
        throw new AssertionError("Expected SHA-256 to be supported", e);
    }
}
Run Code Online (Sandbox Code Playgroud)

(注意:HexFormat在 Java 17 中引入,如果您使用的是早期版本,则需要替代方案。)