项目需要调整图片服务器的解决方案,这次调整使用fastdfs存储小文件,也就是图片,因为现在还没有fastdfs的.net版的客服端,所以理所当然把处理图片放到了java这块,使用java来处理图片并且上传到fastdfs上。
之前还真没用java整过图片,因为一般没这个需求,现在的需求是要根据前台传过来的尺寸进行图片的缩略,自适应缩放,留白,加水印操作。
一开始我使用jdk自带的一套图片处理库来处理图片,也就是sun的JPEGCodec,测试
发现它会把所有的图片全部转换成jpeg格式,也就是说把bmp,gif,png这种格式的图片全部转换成了jpeg格式,而且对于gif动
画图片缩放就只能对第一帧缩放,所以对于gif这种的的缩放完之后就“不动了”。
之后使用Graphics2D+ImageIO的方式,先判断图片的格式,然后缩放的时候强制的把图片格式作为参数传给ImageIO来处理,
ImageIO.write(image, pic_type, out);
但是对于gif动画图片缩放也只对第一帧缩放,所以对于gif这种的的缩放完之后也是“不动了”。
最后在网上发现了一个架包 --gif4j ,它可以对gif动画图片进行缩放但是对于加水印和留白操作就无能为力了。
最后在做压力测试时发现,使用这种方式(java的jdk+gif4j),特别是对于大图的操作,大图我这边测试的是6~7M的图片,发现非常非常耗cpu和
内存,当然程序已经优化过的前提下测试,图片都以流的方式进行操作。但是这种cpu和内存的消耗简直是开玩笑了,要是需要处理的图片一多,每秒过百的请求服务器就得卡死,而且这种方式处理的图片质量不是特别高。
在这种方案放弃之后,发现了ImageMagick。
ImageMagick (TM) 是一个免费的创建、编辑、合成图片的软件。它可以读取、转换、写入多种格式的图片。图片切割、颜色替换、各种效果的应用,图片的旋转、组合,文本,直线,多边形,椭圆,曲线,附加到图片伸展旋转。ImageMagick的大多数功能的使用都来源于命令行工具。它还支持以下程序语言: Perl, C, C++, Python, PHP, Ruby, Java;现成的ImageMagick接口(PerlMagick, Magick++, PythonMagick, MagickWand for PHP, RubyMagick, and JMagick)是可利用的。
利用Java 的JNI(java本地调用),依赖操作系统提供的功能, 操作批量、大图片,这种方式效率比较高。
然后当然是开搞,ImageMagick 在windows和linux下的配置稍有不同,详细情况可以参考:http://www.imagemagick.org/script/index.php
下面贴出使用JMagick调用Imagemagick进行缩略的代码。
public void resize1(String src, String dsec, int width, int height,
int quality) {
ImageInfo info = null;
MagickImage image = null;
Dimension imageDim = null;
MagickImage scaled = null;
try {
info = new ImageInfo(src);
info.setQuality(quality);
image = new MagickImage(info);
imageDim = image.getDimension();
int srcW = imageDim.width;
int srcH = imageDim.height;
int[] i = PicUtil.getWandH(srcW, srcH, width, height, 1);
scaled = image.scaleImage(i[0], i[1]);
scaled.setFileName(dsec);
scaled.writeImage(info);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (scaled != null) {
scaled.destroyImages();
}
if (image != null) {
image.destroyImages();
}
}
}
这里注意,使用完之后要释放资源。
if (scaled != null) {
scaled.destroyImages();
}
if (image != null) {
image.destroyImages();
}
否则会很伤,你懂的。
加水印代码:
public void addWater(String src, String src_biao, String dsrc,
int position, int quality) {
ImageInfo info = null;
ImageInfo logo_info = null;
MagickImage image = null;
MagickImage logo_image = null;
Dimension imageDim = null;
Dimension logo_imageDim = null;
// MagickImage compositeimage = null;
try {
info = new ImageInfo(src);
info.setQuality(quality);
image = new MagickImage(info);
imageDim = image.getDimension();
logo_info = new ImageInfo(src_biao);
logo_image = new MagickImage(logo_info);
logo_imageDim = logo_image.getDimension();
int[] ii = WaterPosition.position(position, imageDim.width,
logo_imageDim.width, imageDim.height, logo_imageDim.height);
image.compositeImage(CompositeOperator.AtopCompositeOp, logo_image,
ii[0], ii[1]);
image.setFileName(dsrc);
image.writeImage(info);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (logo_image != null) {
logo_image.destroyImages();
}
if (image != null) {
image.destroyImages();
}
}
}
代码都整完之后,进行压力测试,问题出来了,java调用JNI的方式存在内存隐患,至于原因,可以网上查查。现在的情况就是如果可以10天半个月
重启下服务,那没问题,但是如果长时间的跑着服务,它会把内存耗尽,然后死掉。
很郁闷,是吧,我也很郁闷。但我想做就做个NB点的吧,然后发现了GraphicsMagick+im4java这种方式。
具体的不介绍了,网上资料很少,
例子代码更少,我在这里贴几段,仅供参考。
缩放代码:
public void resize1(String src, String dsec, int o_width, int o_heigh,
int width, int height, double quality) {
int[] i = PicUtil.getWandH(o_width, o_heigh, width, height, 1);
ConvertCmd cmd = new ConvertCmd(true);
IMOperation op = new IMOperation();
op.addImage(src);
op.scale(i[0], i[1]);
op.quality(quality);
op.addImage(dsec);
try {
cmd.run(op);
} catch (Exception e) {
log.error(e,e);
}
}
加水印:
public void addWater(String src, String src_biao, String dsrc,
int position, double quality) {
CompositeCmd cmd = new CompositeCmd(true);
IMOperation op = new IMOperation();
op.gravity(WaterPosition.positionMagick(position));
op.quality(quality);
op.addImage();
op.addImage();
op.addImage();
try {
cmd.run(op, src_biao, src, dsrc);
} catch (Exception e) {
log.error(e,e);
}
}
我大功告成了!