Java中不解压tar.gz文件获取内部文件MD5哈希的方法
无需解压tar.gz文件获取内部图片MD5哈希值的方法?
我有一个包含大量图片的大型tar.gz文件,需要获取每张图片的MD5哈希值。现有代码可正常处理普通文件夹及图片,但无法获取tar.gz文件内图片的哈希值。尝试直接访问tar.gz内的图片路径时出现以下异常:
Caused by: java.nio.file.FileSystemException: /Users/myuser/old/file.tar.gz/1.jpg: Not a directory at sun.nio.fs.UnixException.translateToIOException(UnixException.java:91) at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102) at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107) at sun.nio.fs.UnixFileAttributeViews$Basic.readAttributes(UnixFileAttributeViews.java:55) at sun.nio.fs.UnixFileSystemProvider.readAttributes(UnixFileSystemProvider.java:144) at java.nio.file.Files.readAttributes(Files.java:1737) at java.nio.file.FileTreeWalker.getAttributes(FileTreeWalker.java:219) at java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:276) at java.nio.file.FileTreeWalker.walk(FileTreeWalker.java:322) at java.nio.file.FileTreeIterator.<init>(FileTreeIterator.java:72) at java.nio.file.Files.walk(Files.java:3574) at java.nio.file.Files.walk(Files.java:3625) at com.example.demo.ImageDeduplication.listFiles(ImageDeduplication.java:78) at com.example.demo.SparkSQL.lambda$1(SparkSQL.java:82) at org.apache.spark.sql.UDFRegistration.$anonfun$register$352(UDFRegistration.scala:775) ... 17 more
当前使用的代码如下:
public static String digestAndBuildImageEntry(Path filePath) throws NoSuchAlgorithmException { try (InputStream is = Files.newInputStream(filePath); BufferedInputStream buffered = new BufferedInputStream(is)) { byte[] data = Files.readAllBytes(filePath); byte[] hashByte = MessageDigest.getInstance("MD5").digest(data); String hash = hashByte.toString(); return hash; } catch (Exception ex) { return null; } }
路径测试情况
- 正常工作的路径:
/Users/myuser/old/1.jpg- 可获取哈希/Users/myuser/old/- 可遍历获取文件夹内所有文件/Users/myuser/old/file.tar.gz- 可获取整个tar.gz文件的哈希
- 无法正常工作的路径:
/Users/myuser/old/file.tar.gz/1.jpg- 提示“Not a directory”
解决方案
可以不用解压tar.gz文件,直接通过流读取内部文件内容计算MD5。推荐使用Apache Commons Compress库处理tar.gz文件,它支持直接读取归档内的条目,无需写入磁盘。
步骤1:引入依赖(Maven)
在pom.xml中添加依赖:
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-compress</artifactId> <version>1.24.0</version> <!-- 使用最新稳定版本 --> </dependency>
步骤2:编写处理代码
核心思路是打开tar.gz的输入流,遍历每个归档条目,判断是否为图片文件,然后通过流分块计算MD5(避免一次性加载大文件到内存):
import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Set; public class TarGzImageHasher { // 定义需要处理的图片后缀 private static final Set<String> IMAGE_EXTENSIONS = Set.of("jpg", "jpeg", "png", "gif", "bmp"); public static void processTarGz(Path tarGzPath) throws IOException, NoSuchAlgorithmException { try (InputStream fis = new FileInputStream(tarGzPath.toFile()); BufferedInputStream bis = new BufferedInputStream(fis); GzipCompressorInputStream gzis = new GzipCompressorInputStream(bis); TarArchiveInputStream tais = new TarArchiveInputStream(gzis)) { TarArchiveEntry entry; while ((entry = tais.getNextTarEntry()) != null) { // 跳过目录和非图片文件 if (entry.isDirectory() || !isImageFile(entry.getName())) { continue; } // 计算当前图片条目的MD5 String md5Hash = calculateMd5(tais); System.out.printf("文件: %s, MD5: %s%n", entry.getName(), md5Hash); } } } private static boolean isImageFile(String fileName) { int dotIndex = fileName.lastIndexOf('.'); if (dotIndex == -1) { return false; } String extension = fileName.substring(dotIndex + 1).toLowerCase(); return IMAGE_EXTENSIONS.contains(extension); } private static String calculateMd5(InputStream inputStream) throws NoSuchAlgorithmException, IOException { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] buffer = new byte[8192]; // 8KB缓冲,适合大文件处理 int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { md.update(buffer, 0, bytesRead); } byte[] hashBytes = md.digest(); // 将字节数组转为标准十六进制字符串(修正原代码中byte[]直接toString的错误) StringBuilder sb = new StringBuilder(); for (byte b : hashBytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } // 测试入口 public static void main(String[] args) throws IOException, NoSuchAlgorithmException { Path tarGzPath = Path.of("/Users/myuser/old/file.tar.gz"); processTarGz(tarGzPath); } }
关键说明
- 内存优化:用8KB缓冲分块读取文件内容,避免一次性加载大型图片到内存引发溢出。
- 哈希值修正:原代码中
hashByte.toString()返回的是对象地址,这里转为标准十六进制哈希字符串。 - 文件过滤:通过后缀名筛选图片文件,跳过目录和非图片条目,提升处理效率。
内容的提问来源于stack exchange,提问作者Anonymous
相关产品推荐
相关产品推荐

