有选择地从 S3 中的 zip 文件中提取条目,而无需下载整个文件

我试图从 S3 中的大量 zip 文件中提取特定项目,而不下载整个文件。

这里的 Python 解决方案:从 S3 读取 ZIP 文件而不下载整个文件似乎可行。一般来说,Java 中的等效底层功能似乎不太宽松,因此我不得不进行各种调整。

在附加的代码中,您可以看到我成功获取了中央目录并将其写入临时文件,Java 的 ZipFile 可以使用该临时文件来迭代 CD 中的 zip 条目。

然而,我坚持夸大单个条目。当前代码抛出错误的标头异常。我需要给充气机本地文件头+压缩内容,还是只是压缩内容?我已经尝试过这两种方法,但显然我要么不使用充气器校正器,要么没有达到预期的效果。

编辑

将 Python 计算与 Java 代码中的计算进行比较,我意识到 Java 偏差了 4。 entry.getExtra().length例如,可能会报告 24,同一条目的 zipinfo 命令行实用程序也是如此。Python 报告 28。我不完全理解这种差异,但 PKWare 规范提到了额外字段的“2 字节标识符和 2 字节数据大小字段”。无论如何,添加一个软糖值 4 让它工作,但我想更多地了解发生了什么 - 添加随机软糖值以使事情正常工作并不能解决: offset += 30 + entry.getName().length() + extra + 4;


陪伴而非守候
浏览 127回答 2
2回答

桃花长相依

我的总体方法是合理的,但由于缺乏从 Java 的 ZipFile 返回的详细信息而受到阻碍。例如,有时在下一个本地标头开始之前,压缩数据的末尾有一个额外的 16 个字节。ZipFile 中没有任何内容可以帮助解决此问题。zip4j 似乎是一个更好的选择,并提供了以下方法: header.getOffsetLocalHeader()消除了一些容易出错的计算。

慕的地10843

我还可以使用 zip4j 通过以下代码使其工作。但是我仍然不明白通过等式的解码部分: long endFile = 30 + offset + header.getFileNameLength() + compressedSize - 1;。30从哪里来?我如何确保该方程包含所有用例的所有必要变量?public static void main(String[] args) throws Exception {        S3Client s3Client = S3Client.builder()                .credentialsProvider(StaticCredentialsProvider                        .create(AwsSessionCredentials.create(ACCESS_KEY, SECRET_KEY, SESSION_TOKEN)))                .region(Region.US_WEST_2)                .build();        HeadObjectResponse headObject = s3Client.headObject(HeadObjectRequest.builder()                .bucket(BUCKET)                .key(OBJECT_PATH)                .build());        long zipSize = headObject.contentLength();        // fetch the last 22 bytes (end-of-central-directory record; assuming the comment field is empty)        long eocdStart = zipSize - 22;        final var eocdStream = s3Client.getObject(GetObjectRequest.builder()                .bucket(BUCKET)                .key(OBJECT_PATH)                .range("bytes=%d-%d".formatted(eocdStart, zipSize))                .build());        System.out.println("eocd start: " + eocdStart);        byte[] eocd = IOUtils.toByteArray(eocdStream);        // get the start offset and size of the central directory        int cdSize = byteArrayToLeInt(Arrays.copyOfRange(eocd, 12, 16));        int cdStart = byteArrayToLeInt(Arrays.copyOfRange(eocd, 16, 20));        System.out.println("cdStart: " + cdStart);        System.out.println("cdSize: " + cdSize);        // get the full central directory        final var cdStream = s3Client.getObject(GetObjectRequest.builder()                .bucket(BUCKET)                .key(OBJECT_PATH)                .range("bytes=%d-%d".formatted(cdStart, cdStart + cdSize - 1))                .build());        byte[] cd = IOUtils.toByteArray(cdStream);        // write the full dir + eocd:        ByteArrayOutputStream out = new ByteArrayOutputStream();        // write cd        out.write(cd);        // write eocd, resetting the cd start to 0 since that is        // where it will appear in our new temp file        byte[] b = leIntToByteArray(0);        eocd[16] = b[0];        eocd[17] = b[1];        eocd[18] = b[2];        eocd[19] = b[3];        out.write(eocd);        out.flush();        byte[] cdbytes = out.toByteArray();        System.out.println(cdbytes.length);        File tempFile = Files.createTempFile("temp", "zip").toFile();        FileOutputStream output = new FileOutputStream(tempFile);        output.write(cdbytes);        output.flush();        output.close();        getZipFile1(s3Client, tempFile, "a2ed09e5-dfdb-4a66-95f5-8bb62bc8fafd-2023-05-23T10_07_19Z.warc.gz");        getZipFile1(s3Client, tempFile, "index.cdx.gz");        getZipFile1(s3Client, tempFile, "index.cdx");        getZipFile1(s3Client, tempFile, "extraPages.jsonl");        getZipFile1(s3Client, tempFile, "pages.jsonl");        getZipFile1(s3Client, tempFile, "datapackage.json");        getZipFile1(s3Client, tempFile, "datapackage-digest.json");}private static void getZipFile1(S3Client s3Client, File tempFile, String file) throws Exception {        ZipFile zipFile = new ZipFile(tempFile);        for (var header : zipFile.getFileHeaders()) {            if (!header.isDirectory()) {                if (header.getFileName().contains(file)) {                    System.out.println(header);                    long offset = header.getOffsetLocalHeader(); // 41489906                    int compressedSize = (int) header.getCompressedSize(); // 171                    long endFile = 30 + offset + header.getFileNameLength() + compressedSize - 1;                    byte[] fileBytes = IOUtils.toByteArray(s3Client.getObject(GetObjectRequest.builder()                            .bucket(BUCKET)                            .key(OBJECT_PATH)                            .range("bytes=%d-%d".formatted(offset, endFile))                            .build()));                    ZipInputStream zipInputStream = new ZipInputStream(new ByteArrayInputStream(fileBytes));                    zipInputStream.getNextEntry(header, true);                    File outputFile = new File("/home/joao/Downloads/folder/" + header.getFileName());                    Files.deleteIfExists(outputFile.toPath());                    FileUtils.copyInputStreamToFile(zipInputStream, outputFile);                }            }        }}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java