在 S3 中上传输入流块后无法解压缩 gzip 文件

Unable to decompress gzipped files after uploading input stream chunks in S3

我想获取我的输入流并以与分段上传器类似的方式将 gzip 压缩的部分上传到 s3。 但是,我想将各个文件部分存储在 S3 中,而不是将这些部分变成一个文件。

为此,我创建了以下方法。 但是,当我尝试 gzip 解压缩每个部分时,gzip 会抛出错误并显示:gzip: file_part_2.log.gz: not in gzip format.

我不确定我是否正确压缩了每个部分?

如果我重新初始化 gzipoutputstream: gzip = new GZIPOutputStream(baos); 并在重置字节数组输出流 baos.reset(); 后设置 gzip.finish() 然后我就可以解压缩每个部分。不确定我为什么需要这样做,gzip 输出流是否有类似的 reset

public void upload(String bucket, String key, InputStream is, int partSize) throws Exception
{
    String row;
    BufferedReader br = new BufferedReader(new InputStreamReader(is, ENCODING));
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    GZIPOutputStream gzip = new GZIPOutputStream(baos);

    int partCounter = 0;
    int lineCounter = 0;
    while ((row = br.readLine()) != null) {
        if (baos.size() >= partSize) {
            partCounter = this.uploadChunk(bucket, key, baos, partCounter);

            baos.reset();
        }else if(!row.equals("")){
            row += '\n';
            gzip.write(row.getBytes(ENCODING));
            lineCounter++;
        }
    }

    gzip.finish();
    br.close();
    baos.close();

    if(lineCounter == 0){
        throw new Exception("Aborting upload, file contents is empty!");
    }

    //Final chunk
    if (baos.size() > 0) {
        this.uploadChunk(bucket, key, baos, partCounter);
    }
}

private int uploadChunk(String bucket, String key, ByteArrayOutputStream baos, int partCounter)
{
    ObjectMetadata metaData = new ObjectMetadata();
    metaData.setContentLength(baos.size());

    String[] path = key.split("/");
    String[] filename = path[path.length-1].split("\.");

    filename[0] = filename[0]+"_part_"+partCounter;

    path[path.length-1] = String.join(".", filename);

    amazonS3.putObject(
            bucket,
            String.join("/", path),
            new ByteArrayInputStream(baos.toByteArray()),
            metaData
    );

    log.info("Upload chunk {}, size: {}", partCounter, baos.size());

    return partCounter+1;
}

问题是您对所有块使用单个 GZipOutputStream。所以你实际上是在编写一个 GZip 文件的片段,这些片段必须重新组合才能有用。

对现有代码进行最小程度的更改:

if (baos.size() >= partSize) {
    gzip.close(); 
    partCounter = this.uploadChunk(bucket, key, baos, partCounter);
    baos = baos = new ByteArrayOutputStream();
    gzip = new GZIPOutputStream(baos);
}

你需要在循环结束时做同样的事情。此外,如果行计数器为 0,则不应抛出异常:文件完全有可能完全可分为一定数量的块。

为了改进代码,我将 GZIPOutputStream 包装在 OutputStreamWriterBufferedWriter 中,这样您就不需要显式地进行字符串字节转换。

最后,不要使用 ByteArrayOutputStream.reset()。与创建新流相比,它不会为您节省任何东西,如果您忘记重置,则会为错误打开大门。