Java BufferedImage JPG压缩而不写入文件

8

我看到过几个示例,使用Java BufferedImage对象将压缩的JPG图像写入文件,但是否可能在不写入文件的情况下执行JPG压缩?也许可以通过像这样写入ByteArrayOutputStream来实现?

ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("jpg").next();
ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
jpgWriteParam.setCompressionQuality(0.7f);

ImageOutputStream outputStream = createOutputStream();
jpgWriter.setOutput(outputStream);
IIOImage outputImage = new IIOImage(image, null, null);

// in this example, the JPG is written to file...
// jpgWriter.write(null, outputImage, jpgWriteParam);
// jpgWriter.dispose();

// ...but I want to compress without saving, such as
ByteArrayOutputStream compressed = ???
1个回答

17

只需将您的ByteArrayOutputStream传递给ImageIO.createImageOutputStream(...),如下所示:

// The important part: Create in-memory stream
ByteArrayOutputStream compressed = new ByteArrayOutputStream();

try (ImageOutputStream outputStream = ImageIO.createImageOutputStream(compressed)) {

    // NOTE: The rest of the code is just a cleaned up version of your code

    // Obtain writer for JPEG format
    ImageWriter jpgWriter = ImageIO.getImageWritersByFormatName("JPEG").next();

    // Configure JPEG compression: 70% quality
    ImageWriteParam jpgWriteParam = jpgWriter.getDefaultWriteParam();
    jpgWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    jpgWriteParam.setCompressionQuality(0.7f);

    // Set your in-memory stream as the output
    jpgWriter.setOutput(outputStream);

    // Write image as JPEG w/configured settings to the in-memory stream
    // (the IIOImage is just an aggregator object, allowing you to associate
    // thumbnails and metadata to the image, it "does" nothing)
    jpgWriter.write(null, new IIOImage(image, null, null), jpgWriteParam);

    // Dispose the writer to free resources
    jpgWriter.dispose();
}

// Get data for further processing...
byte[] jpegData = compressed.toByteArray();

提示:默认情况下,ImageIO 在创建 ImageOutputStream 时将使用磁盘缓存。这可能会减慢您的内存流写入速度。要禁用它,请使用 ImageIO.setCache(false)(全局禁用磁盘缓存)或显式创建一个 MemoryCacheImageOutputStream(本地缓存),就像这样:

ImageOutputStream outputStream = new MemoryCacheImageOutputStream(compressed);

谢谢@haraldK!我不完全确定这里发生了什么。如果JPG数据已压缩并写入名为“compressed”的ByteArrayOutputStream,则“IIOImage”部分是做什么的? - JeffThompson
1
@JeffThompson 我已经为您更新了答案。 IIOImage 并不会“做”什么,它只包含你想要写入的数据。如果你想要了解更多信息,我建议你阅读 API 文档或跟随教程。 :-) - Harald K
2
@downwoter请不要因为您认为问题不值得回答而“惩罚”一个完全有效和正确的答案。相反,可以对问题进行投票或者关闭它。 - Harald K
再次感谢您!我确实查看了文档,但是所有用于存储和操作文件流的不同对象非常令人困惑。这样更容易理解!我运行时遇到了内存耗尽的错误,没有使用MemoryCacheImageOutputStream,供您参考。 - JeffThompson
如何在这个方法上编写单元测试?有什么建议吗? - Ravi S.
这个问题可能需要一个比评论更长的答案... 把它作为一个SO问题提出来吧!或者你可以查看TwelveMonkeys ImageIO中的测试,以此获得灵感,其中应该包含很多类似于这个的代码测试。 - Harald K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接