【问题标题】:How can I gzip an InputStream and return an InputStream?如何 gzip 一个 InputStream 并返回一个 InputStream?
【发布时间】:2016-12-19 20:36:31
【问题描述】:

我从 HTTP 请求的响应开始:

InputStream responseInputStream = response.getEntityInputStream()

我需要对该响应进行 gzip 压缩,以便将其上传到 s3 并将其压缩保存:

this.s3.putObject(new PutObjectRequest(bucketName, key, gzippedResponseInputStream, meta));

我知道我可以从responseInputStream 中获取byte[] 数组,然后将它们压缩成一个新的InputStream。但是,对于大量数据,这可能会非常低效。

我知道在 SO 上也有人问过类似的问题,但我没有找到任何似乎可以解决以 InputStream 开头并以压缩后的 InputStream 结尾的特定需求。

感谢您的帮助!

【问题讨论】:

  • 你检查过这个答案吗? stackoverflow.com/a/19326492/2588800
  • 哇,谢谢@SvetlinZarev - 成功了!我一直在搜索所以尝试了我发现的每一个“解决方案”,但到目前为止都没有奏效,但你的建议成功了!谢谢!

标签: java inputstream outputstream gzipinputstream gzipoutputstream


【解决方案1】:

我想你正在寻找PipedInputStream

这是如何做到的。

public InputStrema getGZipStream() {
    final PipedOutputStream pos = new PipedOutputStream();
    PipedInputStream pis = new PipedInputStream();

    try (final InputStream responseInputStream = response.getEntityInputStream();
    ){
        pis.connect(pos);

        Thread thread = new Thread() {
            public void run () {
                startWriting(pos, responseInputStream);
            }
        };
        thread.start();
    } catch(Exception e) {
        e.printStackTrace();
    }

    return pis;
}

public void startWriting(OutputStream out, InputStream in) {
    try (GZIPOutputStream gOut = GZIPOutputStream(out);) {
        byte[] buffer = new byte[10240];
        int len = -1;
        while ((len = in.read(buffer)) != -1) {
            gOut.write(buffer, 0, len);
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        try {
            out.close();
        } catch( Exception e) {
            e.printStackTrace();
        }
    }
}

我还没有测试过这段代码,如果可行,请告诉我。

【讨论】:

  • 它不会工作,因为当您退出 try 主体时您将关闭 pis - 所以您将返回一个关闭的流;
  • 我也在想同样的事情。安全的 Try 是否在方法结束时关闭流?无论如何,我会修改代码以使用它。
  • 改了,至少现在应该没有闭流问题了。
  • 谢谢,如果其他解决方案有任何困难,我会尝试一下。我很欣赏快速响应,我相信这对其他人也有用。
【解决方案2】:
public final class Example {
    public static void main(String[] args) throws IOException, InterruptedException {
        final PipedInputStream inputStream = new PipedInputStream();
        final PipedOutputStream outputStream = new PipedOutputStream(inputStream);

        Thread compressorThread = new Thread() {
            @Override
            public void run() {
                try (FileInputStream dataSource = new FileInputStream(args[0])) {
                    try (GZIPOutputStream sink = new GZIPOutputStream(outputStream)) {
                        final byte[] buffer = new byte[8 * 1024];
                        for (int bytesRead = dataSource.read(buffer); bytesRead >= 0; bytesRead = dataSource.read(buffer)) {
                            sink.write(buffer, 0, bytesRead);
                        }
                    }
                } catch (IOException ex) {
                    //TODO handle exception -> maybe use callable + executor
                }
            }
        };
        compressorThread.start();

        try (FileOutputStream destination = new FileOutputStream(args[1])) {
            final byte[] buffer = new byte[8 * 1024];
            for (int bytesRead = inputStream.read(buffer); bytesRead >= 0; bytesRead = inputStream.read(buffer)) {
                destination.write(buffer, 0, bytesRead);
            }
        }

        compressorThread.join();
    }

}

你是对的,我之前的例子是错误的。您可以使用管道流。这里的问题是您不能使用来自同一线程的输入和输出流。也不要忘记在写作线程上join()。您可以通过提供两个参数来测试我的示例:

  • args[0] -> 源文件
  • args[1] -> 写入压缩内容的目的地

PS:@11thdimension 使用他的管道流解决方案快了几分钟,所以如果你觉得这有帮助,请接受他的回答

【讨论】:

  • GZIPInputStream 用于获取已经压缩的数据并对其进行解压缩。在意识到这一点之前,我实际上已经尝试过了。但是,它在文档中也说得很清楚。感谢您的快速响应,但这是不正确的。
  • 谢谢@svetlin 我结束了你在问题评论中找到的解决方案,但我很感激这一点,我相信它有效,所以我会将其标记为“已接受”。
猜你喜欢
  • 1970-01-01
  • 2012-12-27
  • 1970-01-01
  • 2014-03-31
  • 2011-06-07
  • 1970-01-01
  • 2018-08-22
  • 1970-01-01
相关资源
最近更新 更多