java 使用ImageIO.writer从BufferedImage生成jpeg图像遇到问题总结及解决
生成jpeg图像这是个非常非常简单的东西了,网上很多介绍是直接用com.sun.image.codec.jpeg.JPEGImageEncoder来实现,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
/**
* 将原图压缩生成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数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
/**
* 将原图压缩生成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。
参考网上别人的写法改成这样就没问题了:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
/**
* 将{@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,如果失败,就用第二种方式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/**
* 将{@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();
}
|
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!