如何从S3获取的Zip/tar.gz文件流中仅列出所有文件名?
基于S3部分下载提取Zip文件文件名的方案
针对你的需求,完全可以利用S3的范围请求(Range)结合Zip格式的特性,只下载包含元信息的关键区域,就能提取所有文件的相对路径,无需下载完整压缩包,大幅节省带宽。
核心原理
Zip格式的文件元数据(包括文件名)集中存储在中央目录(Central Directory),而中央目录的位置由文件尾部的**EOCD(End of Central Directory Record)**标记指定。通过两次范围请求:先读取文件尾部找到EOCD,再读取完整的中央目录,就能解析出所有文件名。
具体步骤
- 第一步:获取文件总大小
调用S3的headObject接口获取文件的Content-Length,这一步仅获取元数据,不下载任何文件内容。 - 第二步:读取文件尾部找EOCD
EOCD通常位于文件最后几百字节内,发起范围请求获取最后1024字节(可根据实际情况调整),解析出中央目录的起始偏移和大小。 - 第三步:读取中央目录并解析文件名
根据上一步得到的偏移和大小,发起范围请求下载中央目录区域,解析该区域即可提取所有Zip条目的文件名。
推荐工具库
- Apache Commons Compress:提供了成熟的Zip格式解析工具,比如
CentralDirectoryParser可以直接解析EOCD和中央目录,无需手动处理底层字节逻辑。 - Apache Commons IO:简化流与字节数组的转换操作,避免手动处理流关闭等繁琐工作。
代码示例
import org.apache.commons.compress.archivers.zip.CentralDirectory; import org.apache.commons.compress.archivers.zip.CentralDirectoryParser; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; import org.apache.commons.io.IOUtils; import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.HeadObjectRequest; import software.amazon.awssdk.services.s3.model.HeadObjectResponse; import java.io.ByteArrayInputStream; import java.io.IOException; public class S3ZipFilenameExtractor { public static void extractFilenames(S3Client s3Client, String bucket, String key) throws IOException { // 1. 获取文件总大小 HeadObjectResponse headResp = s3Client.headObject(HeadObjectRequest.builder() .bucket(bucket) .key(key) .build()); long totalFileSize = headResp.contentLength(); // 2. 请求文件尾部1024字节,解析EOCD long tailStart = Math.max(0, totalFileSize - 1024); GetObjectRequest tailReq = GetObjectRequest.builder() .bucket(bucket) .key(key) .range(String.format("bytes=%d-%d", tailStart, totalFileSize - 1)) .build(); ResponseInputStream<?> tailStream = s3Client.getObject(tailReq); byte[] tailBytes = IOUtils.toByteArray(tailStream); CentralDirectoryParser parser = new CentralDirectoryParser(); parser.parse(tailBytes, 0, tailBytes.length); CentralDirectory centralDir = parser.getCentralDirectory(); long cdStartOffset = centralDir.getStartOfCentralDirectory(); long cdTotalSize = centralDir.getSizeOfCentralDirectory(); // 3. 请求中央目录区域,解析文件名 GetObjectRequest cdReq = GetObjectRequest.builder() .bucket(bucket) .key(key) .range(String.format("bytes=%d-%d", cdStartOffset, cdStartOffset + cdTotalSize - 1)) .build(); ResponseInputStream<?> cdStream = s3Client.getObject(cdReq); byte[] cdBytes = IOUtils.toByteArray(cdStream); try (ZipArchiveInputStream zipIn = new ZipArchiveInputStream(new ByteArrayInputStream(cdBytes))) { ZipArchiveEntry entry; while ((entry = zipIn.getNextZipEntry()) != null) { if (!entry.isDirectory()) { String filename = entry.getName(); // 这里处理文件名逻辑 System.out.println(filename); } } } } }
注意事项
- 该方案仅适用于标准Zip格式,分卷压缩、加密压缩等特殊格式可能无法正常解析。
- 如果文件尾部的1024字节不足以包含EOCD,可以适当扩大请求的尾部长度,或者循环递增长度直到找到EOCD标记。
- 确保使用的S3客户端支持范围请求,AWS SDK默认支持该功能。
内容的提问来源于stack exchange,提问作者沈小虎
相关产品推荐
相关产品推荐

