java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台(即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se))的总称。
这篇文章主要介绍了 java 使用 ImageIO.writer 从 BufferedImage 生成 jpeg 图像遇到问题总结及解决的相关资料, 需要的朋友可以参考下
java 使用 ImageIO.writer 从 BufferedImage 生成 jpeg 图像遇到问题总结及解决
生成 jpeg 图像这是个非常非常简单的东西了,网上很多介绍是直接用 com.sun.image.codec.jpeg.JPEGImageEncoder 来实现,如下:
- /**
- * 将原图压缩生成jpeg格式的数据
- * @param source
- * @return
- */
- public static byte[] wirteJPEGBytes(BufferedImage source) {
- if (null == source) throw new NullPointerException();
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- JPEGImageEncoder jencoder = JPEGCodec.createJPEGEncoder(output);
- JPEGEncodeParam param = jencoder.getDefaultJPEGEncodeParam(source);
- param.setQuality(0.75f, true);
- jencoder.setJPEGEncodeParam(param);
- try {
- jencoder.encode(source);
- } catch(ImageFormatException e) {
- throw new RuntimeException(e);
- } catch(IOException e) {
- throw new RuntimeException(e);
- }
- return output.toByteArray();
- }
JPEGImageEncoder 只是 sun 的 jpeg 编码实现,并不是标准的 Java API, 只在 sun jvm 中被支持,但在其他的 jvm 上,并不会被支持。
而且,虽然上面的代码在 Java 1.6,1.7 上都能正常执行,但在如果使用 java 1.8,上面这个代码会报错:
访问限制:由于对必需的库 C:\Program Files\Java\jdk1.8.0_111\jre\lib\rt.jar 具有一定限制,因此无法访问类型 JPEGImageEncoder
所以这个方法是有局限性的。
走捷径是不行的,还是得规规矩矩按 java 的规范来做,ImageIO 类中提供了 ImageIO.writer 方法可以生成指定的格式的图像,才是正规的实现方式。
但是使用 ImageIO.writer 方法也是有讲究的。
我原先是这样写的,就是简单的调用 ImageIO.writer 方法生成 jpeg 数据:
- /**
- * 将原图压缩生成jpeg格式的数据
- * @param source
- * @return
- * @see #wirteBytes(BufferedImage, String)
- */
- public static byte[] wirteJPEGBytes(BufferedImage source) {
- return wirteBytes(source, "JPEG");
- }
- /**
- * 将原图压缩生成jpeg格式的数据
- * @param source
- * @return
- * @see #wirteBytes(BufferedImage, String)
- */
- public static byte[] wirteJPEGBytes(BufferedImage source) {
- return wirteBytes(source, "JPEG");
- }
- /**
- * 将{@link BufferedImage}生成formatName指定格式的图像数据
- * @param source
- * @param formatName 图像格式名,图像格式名错误则抛出异常
- * @return
- */
- public static byte[] wirteBytes(BufferedImage source, String formatName) {
- Assert.notNull(source, "source");
- Assert.notEmpty(formatName, "formatName");
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- try {
- if (!ImageIO.write(source, formatName.toLowerCase(), output))
- // 返回false则抛出异常
- throw new IllegalArgumentException(String.format("not found writer for '%s'", formatName));
- } catch(IOException e) {
- throw new RuntimeException(e);
- }
- return output.toByteArray();
- }
处理了几万张图像文件都没问题,遇到一张 png 图像,ImageIO.write 居然返回 false, 抛出异常了。
究其原因,是 ImageIO.wite 方法在中调用的私有方法 getWriter 寻找合适的 ImageWriter 时不仅与 formatName 相关,还是输入的原图有关 (具体是怎么相关的,因为逻辑关系太复杂没有深究), 造成 getWriter 方法找不到对应的 ImageWriter。
参考网上别人的写法改成这样就没问题了:
- /**
- * 将{@link BufferedImage}生成formatName指定格式的图像数据
- * @param source
- * @param formatName 图像格式名,图像格式名错误则抛出异常
- * @return
- */
- public static byte[] wirteBytes(BufferedImage source, String formatName) {
- Assert.notNull(source, "source");
- Assert.notEmpty(formatName, "formatName");
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- BufferedImage newBufferedImage = new BufferedImage(source.getWidth(), source.getHeight(), BufferedImage.TYPE_INT_RGB);
- Graphics2D g = newBufferedImage.createGraphics();
- try {
- g.drawImage(source, 0, 0, null);
- if (!ImageIO.write(newBufferedImage, formatName, output)) throw new IllegalArgumentException(String.format("not found writer for '%s'", formatName));
- } catch(IOException e) {
- throw new RuntimeException(e);
- } finally {
- g.dispose();
- }
- return output.toByteArray();
- }
基本的思路就是重创建一个大小相同的 BufferedImage,然后用 Graphics.drawImage 方法将原图写入新的 BufferedImage 对象,通过这一道转换,抹平了,不同类型图像格式生成的 BufferedImage 对象之间的区别,再调用 ImageIO.write 对新的 ImageIO.write 对象进行图像处理就不会有问题了。
改进
在我的项目中图像数据是从互联网上搜索到的,遇到的图像格式绝大部分都是 jpeg, 但也有少量的 png,bmp 等格式,对于占绝大多数的 jpeg 图像来说,我最开始的方法都是有效的,而上面的这个方法多出一道工序就显得有些多余,还浪费资源,所以又改进了上述的方法,基本的原理就是先尝试直接 ImageIO.write 来生成 jpeg,如果失败,就用第二种方式。
- /**
- * 将{@link BufferedImage}生成formatName指定格式的图像数据
- * @param source
- * @param formatName 图像格式名,图像格式名错误则抛出异常
- * @return
- */
- public static byte[] wirteBytes(BufferedImage source,String formatName){
- Assert.notNull(source, "source");
- Assert.notEmpty(formatName, "formatName");
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- Graphics2D g = null;
- try {
- for(BufferedImage s=source;!ImageIO.write(s, formatName, output);){
- if(null!=g)
- throw new IllegalArgumentException(String.format("not found writer for '%s'",formatName));
- s = new BufferedImage(source.getWidth(),
- source.getHeight(), BufferedImage.TYPE_INT_RGB);
- g = s.createGraphics();
- g.drawImage(source, 0, 0,null);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- if (null != g)
- g.dispose();
- }
- return output.toByteArray();
- }
来源: http://www.phperz.com/article/17/1219/357870.html