欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

java 使用ImageIO.writer从BufferedImage生成jpeg图像遇到问题总结及解决

程序员文章站 2024-03-04 16:15:17
java 使用imageio.writer从bufferedimage生成jpeg图像遇到问题总结及解决 生成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.writer从BufferedImage生成jpeg图像遇到问题总结及解决

所以这个方法是有局限性的。

走捷径是不行的,还是得规规矩矩按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();    
  }

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!