You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何利用已打开的Java InputStream同时复制文件并计算哈希值?

能不能用同一个已打开的InputStream同时执行复制文件与计算校验和?

答案是可以,但不能像你现在这样分开调用getFileSHA2Checksum和transferTo两个方法——因为InputStream是单向流式读取的,一旦读完一次,流的指针就会移动到末尾,后续再读取就拿不到数据了。

问题根源

你当前的两个方法都是独立读取InputStream:

  • getFileSHA2Checksum会把InputStream的内容全部读完来计算校验和
  • 之后再调用transferTo时,InputStream已经没有数据可以读取了,复制操作会生成空文件

正确实现方式:一次读取同时完成两个操作

最合理的做法是在同一次循环读取中,既把数据写入目标文件,又更新校验和的MessageDigest,这样只需要读取一次InputStream,效率更高,也避免了流指针的问题。

修改后的Uploader类示例:

public class Uploader {

    private static final String SHA_256 = "SHA-256";
    private File file2; // 确保这个变量已正确初始化

    // 新增:同时完成文件复制与校验和计算的方法
    public String transferAndCalculateChecksum(InputStream fis) throws IOException {
        MessageDigest md;
        try {
            md = MessageDigest.getInstance(SHA_256);
        } catch (NoSuchAlgorithmException e) {
            return "KO";
        }

        byte[] buffer = new byte[8192]; // 8KB缓冲区,可根据需求调整
        int bytesRead;
        // 用try-with-resources自动关闭FileOutputStream
        try (FileOutputStream fos = new FileOutputStream(file2)) {
            while ((bytesRead = fis.read(buffer)) != -1) {
                md.update(buffer, 0, bytesRead); // 更新校验和
                fos.write(buffer, 0, bytesRead); // 写入文件
            }
        }

        // 将字节数组形式的校验和转换成十六进制字符串
        StringBuilder checksumSb = new StringBuilder();
        for (byte b : md.digest()) {
            checksumSb.append(String.format("%02x", b));
        }
        return checksumSb.toString();
    }

    // 保留原有方法,但注意:不可用同一个InputStream连续调用这些方法
    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);
    }

    private String getFileChecksum(MessageDigest digest, InputStream fis) throws IOException {
        byte[] byteArray = new byte[1024];
        int bytesCount = 0;
        while ((bytesCount = fis.read(byteArray)) != -1) {
            digest.update(byteArray, 0, bytesCount);
        }
        byte[] bytes = digest.digest();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1));
        }
        return sb.toString();
    }
}

小文件的折中方案(不推荐用于大文件)

如果你的文件体积很小,可以先把InputStream的全部内容读取到字节数组中,再用ByteArrayInputStream包装成可重复读取的流,分别传入两个方法:

// 仅适用于小文件,大文件会占用过多内存
byte[] fileContent = IOUtils.toByteArray(fis);
String checksum = uploader.getFileSHA2Checksum(new ByteArrayInputStream(fileContent));
uploader.transferTo(new ByteArrayInputStream(fileContent));

但这种方式会把整个文件加载到内存,对于大文件(比如几百MB以上)会导致内存占用过高,因此不推荐。

内容的提问来源于stack exchange,提问作者Jordi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 18:23:33