寻找比ImageIO.write更快的BufferedImage转GIF字节方案
问题
我用ImageIO.write时发现它占用硬盘资源较多,了解到jDeli、Apache Commons、JAI这类工具处理速度更快,但jDeli成本太高。我需要把编码后的字节用于自定义远程桌面工具,现有转换代码如下:
public static byte[] imageToJPEGByteArray(Image aImage, int width, int height, int qualityPercent) throws IOException { byte[] imageBytes = new byte[0]; float quality = 75 / 100f; BufferedImage destImage; destImage = SwingFXUtils.fromFXImage(aImage, null); // Output JPEG byte array ByteArrayOutputStream baos = new ByteArrayOutputStream(); if(qualityPercent != -1) { // Start to create JPEG with quality option ImageWriter writer = null; Iterator iter = ImageIO.getImageWritersByFormatName("gif"); if (iter.hasNext()) { writer = (ImageWriter) iter.next(); } ImageOutputStream ios = ImageIO.createImageOutputStream(baos); writer.setOutput(ios); ImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault()); iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); iwparam.setCompressionQuality(quality); writer.write(null, new IIOImage(destImage, null, null), iwparam); ios.flush(); writer.dispose(); ios.close(); // Done creating JPEG with quality option } else { // This one line below created a JPEG file without quality option ImageIO.write(destImage, "gif", baos); } baos.flush(); imageBytes = baos.toByteArray(); baos.close(); // Done return imageBytes; }
解决方案
先修复原生ImageIO代码的bug
你的代码里有几个明显问题:
- 要转JPEG却用了
"gif"格式的ImageWriter,格式完全错配 quality变量固定写死75%,没用到传入的qualityPercent参数- 未处理writer为null的异常情况
修复后的代码:
public static byte[] imageToJPEGByteArray(Image aImage, int width, int height, int qualityPercent) throws IOException { // 使用传入的qualityPercent,默认75% float quality = qualityPercent != -1 ? qualityPercent / 100f : 0.75f; BufferedImage destImage = SwingFXUtils.fromFXImage(aImage, null); // 用try-with-resources自动关闭流,避免资源泄漏 try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) { // 获取JPEG格式的ImageWriter ImageWriter writer = ImageIO.getImageWritersByFormatName("jpeg").next(); try { writer.setOutput(ios); JPEGImageWriteParam iwparam = new JPEGImageWriteParam(Locale.getDefault()); iwparam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); iwparam.setCompressionQuality(quality); writer.write(null, new IIOImage(destImage, null, null), iwparam); ios.flush(); } finally { writer.dispose(); } return baos.toByteArray(); } }
用Apache Commons Imaging替代(免费开源)
Apache Commons Imaging是免费开源库,编码速度比原生ImageIO更快,资源占用更低,适合远程桌面这种对性能敏感的场景。
示例代码:
import org.apache.commons.imaging.Imaging; import org.apache.commons.imaging.formats.jpeg.JpegConstants; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.util.HashMap; import java.util.Map; public static byte[] imageToJPEGByteArrayWithCommons(Image aImage, int qualityPercent) throws Exception { float quality = qualityPercent != -1 ? qualityPercent / 100f : 0.75f; BufferedImage destImage = SwingFXUtils.fromFXImage(aImage, null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); Map<String, Object> params = new HashMap<>(); params.put(JpegConstants.PARAM_KEY_QUALITY, quality); Imaging.writeImage(destImage, baos, org.apache.commons.imaging.ImageFormats.JPEG, params); return baos.toByteArray(); }
用JAI Image I/O替代(免费开源)
JAI Image I/O是原生ImageIO的扩展,提供更高效的编码实现,同样免费开源。需要先添加对应的依赖包(比如jai-imageio-core)。
示例代码:
import javax.imageio.ImageIO; import javax.imageio.ImageWriter; import javax.imageio.stream.ImageOutputStream; import javax.imageio.IIOImage; import com.sun.media.imageio.plugins.jpeg.JPEGImageWriteParam; import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.util.Iterator; import java.util.Locale; public static byte[] imageToJPEGByteArrayWithJAI(Image aImage, int qualityPercent) throws Exception { float quality = qualityPercent != -1 ? qualityPercent / 100f : 0.75f; BufferedImage destImage = SwingFXUtils.fromFXImage(aImage, null); ByteArrayOutputStream baos = new ByteArrayOutputStream(); Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg"); ImageWriter writer = writers.next(); try (ImageOutputStream ios = ImageIO.createImageOutputStream(baos)) { writer.setOutput(ios); JPEGImageWriteParam param = new JPEGImageWriteParam(Locale.getDefault()); param.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT); param.setCompressionQuality(quality); writer.write(null, new IIOImage(destImage, null, null), param); ios.flush(); return baos.toByteArray(); } finally { writer.dispose(); } }
内容的提问来源于stack exchange,提问作者orkim okard
相关产品推荐
相关产品推荐

