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



我想采用我的输入流并以类似于分段上传器的方式将 gzip 的部分上传到 s3。但是,我想将各个文件部分存储在 S3 中,而不是将这些部分转换为单个文件。

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

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

如果我重新初始化 gzipoutputstream: gzip = new GZIPOutputStream(baos); 并在重置字节数组输出流后设置gzip.finish() baos.reset();那么我就可以解压缩每个部分。不知道为什么我需要这样做,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。所以你实际上是在写一个GZiped文件的各个片段,这些文件必须重新组合才能有用。

对现有代码进行最少的更改:

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() .它不会为您节省任何费用,而不仅仅是创建新流,如果您忘记重置,它会为错误打开大门。

最新更新