Java: 如何在正在保存的流上即时计算 sha1 摘要?

Java: how to calculate sha1 digest on-the-fly on a stream that is being saved?

我有一个用 Java 编写的 servlet,它接受需要保存在 MongoDb/GridFS 中的多部分形式的发布文件。我已经有了为此工作的代码。

这是一个代码片段,展示了如何使用 org.apache.commons.fileupload 包完成它。它几乎不消耗内存,因为它不会在内存中保留太多数据。

        ServletFileUpload upload = new ServletFileUpload();
        FileItemIterator iter = upload.getItemIterator(req);
        while (iter.hasNext()) {
            FileItemStream item = iter.next();
            String name = item.getFieldName();
            InputStream stream = item.openStream();
            if (item.isFormField()) {
                toProcess.put(name, Streams.asString(stream));
            } else {
                String fileName = item.getName();
                String contentType = item.getHeaders().getHeader("Content-Type");
                GridFSUploadOptions options = new GridFSUploadOptions()
                        // .chunkSizeBytes(358400)
                        .metadata(new Document("content_type", contentType));
                ObjectId fileId = gridFSFilesBucket.uploadFromStream(fileName, stream, options);
                fileIds.add(fileId);
                fileNames.add(fileName);
            }

我还需要计算所有文件的 sha1 哈希值。 Apache digestutils 可以用于此。它有一个可以在流上计算 sha1 的方法:

https://commons.apache.org/proper/commons-codec/apidocs/org/apache/commons/codec/digest/DigestUtils.html#sha1-java.io.InputStream-

我的问题是这个方法完全消耗了流。我需要将输入流分成两部分。将一部分送入 SHA-1 计算,将另一部分送入 GridFS 存储桶。

我该怎么做?我正在考虑创建自己的 "pipe",它具有输入和输出流,转发所有数据但动态更新摘要。

就是不知道怎么开始写这样的管道

您可以使用 Java API class DigestInputStream

正如 Javadoc 所解释的,

A transparent stream that updates the associated message digest using the bits going through the stream.

To complete the message digest computation, call one of the digest methods on the associated message digest after your calls to one of this digest input stream's read methods.

在您的代码中,您可以这样做:

InputStream stream = item.openStream();
MessageDigest digest = MessageDigest.getInstance("SHA-256");
stream = new DigestInputStream(stream, digest);

最后你可以得到摘要:

byte[] hash = digest.digest();