预览图生成策略

来源:互联网 发布:时间悖论 知乎 编辑:程序博客网 时间:2024/06/06 06:53
关键字: 预览图
有一段时间内存老涨,跟踪到图片上传可能有问题
测了一下:

LoadRunner场景:
每10秒加16个VUser
VUser总量800
分4台PC代理执行
全部robot相册表单上传图片一个功能点

分步压力测试:
1.使用现有程序
20多分钟后大概上传了5000多张1024*768的桌面图片后,
jprofiler显示内存堆占据极大,主要是byte类占据
top一下,发现java进程占了1.9G内存,而且还在涨。
负载也很高:load average: 24.18, 0.15, 0.09

2.将上传图片功能完全注释掉,只剩下空壳
半个小时后,内存保持在600M-800M
负载正常:load average: 0.07, 0.04, 0.15
正常
如此看来上传图片功能是有问题。

3.将上传图片功能留下,把其后的生成预览图注释掉。
半个小时后,内存保持在800M-900M
正常
如此看来commons-upload没问题(否则就要试一下改用cos等上传组件),
可能是生成预览图有问题

检查生成预览图程序,是直接用J2D的Graphic2D将图画出来,用iio保存的

优化:
将预览图程序重构为策略模式。
将所有可能方案作为策略实现,一个个试。
通过查资料,
找到:awt,j2d,jai,iio,jmagick,imagej,imagemanip等方案。

Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.Dimension;  
  4. import java.io.File;  
  5. import java.io.IOException;  
  6.   
  7. import org.apache.commons.io.FileUtils;  
  8. import org.apache.commons.logging.Log;  
  9. import org.apache.commons.logging.LogFactory;  
  10.   
  11. /** 
  12.  *  
  13.  * 此类负责与调用者打交道 
  14.  *  
  15.  * @author 梁飞 liangfei0201@163.com 
  16.  * 
  17.  */  
  18. public class PreivewManager {  
  19.       
  20.     protected static final Log log = LogFactory.getLog(PreivewManager.class);  
  21.       
  22.     private PreviewGenerator previewGenerator;  
  23.       
  24.     public PreivewManager(PreviewGenerator previewGenerator) {  
  25.         this.previewGenerator = previewGenerator;  
  26.     }  
  27.       
  28.     /** 
  29.      * 一些通用的处理就在这先做了 
  30.      *  
  31.      * @param source 
  32.      * @param target 
  33.      * @param width 
  34.      * @param height 
  35.      * @throws IOException 
  36.      */  
  37.     public void generatePreview(String source, String target, int width, int height) throws IOException {  
  38.         if (source == null || ! new File(source).exists()) {  
  39.             return;  
  40.         }  
  41.         checkDir(target);  
  42.           
  43.         Dimension d = previewGenerator.getSize(source);  
  44.         int w = (int)d.getWidth();  
  45.         int h = (int)d.getHeight();  
  46.         if (w <= width && h <= height) {  
  47.             copy(source, target); //如果图片较小,就直接copy过去  
  48.         } else {  
  49.             //同比缩放  
  50.             if (w > width || h > height) {  
  51.                 if(w * height > h * width){  
  52.                     height = h * width / w;  
  53.                 }else{  
  54.                     width = w * height / h;  
  55.                 }  
  56.             } else {  
  57.                 width = w;  
  58.                 height = h;  
  59.             }  
  60.             previewGenerator.generate(source, target, width, height);  
  61.         }  
  62.         return ;  
  63.     }  
  64.       
  65.     private void checkDir(String target) {  
  66.         File dir = new File(target).getParentFile();  
  67.         if (! dir.exists()) {  
  68.             dir.mkdirs();  
  69.             log.warn(dir.getAbsolutePath() + " not exists! already auto made!");  
  70.         }  
  71.     }  
  72.       
  73.     private void copy(String source, String target) throws IOException {  
  74.         FileUtils.copyFile(new File(source), new File(target));  
  75.     }  
  76. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.Dimension;  
  4. import java.io.IOException;  
  5.   
  6. /** 
  7.  *  
  8.  * 预览图生成的策略接口 
  9.  *  
  10.  * @author 梁飞 liangfei0201@163.com 
  11.  * 
  12.  */  
  13.   
  14. public interface PreviewGenerator {  
  15.       
  16.     /** 
  17.      * 主要用于判断是否为大图,小图就copy 
  18.      * @param source 源图片位置 
  19.      * @return Dimension 图片的大小 
  20.      * @throws IOException 
  21.      */  
  22.     public Dimension getSize(String source) throws IOException;  
  23.       
  24.     /** 
  25.      * 处理生成预览图的策略方法 
  26.      * 大小判定,比例调整已在PreivewManager,请直接使用width,height 
  27.      * @param source 源图片位置 
  28.      * @param target 保存预览图位置 
  29.      * @param width 预览图宽度 
  30.      * @param height 预览图高度 
  31.      * @throws IOException 
  32.      */  
  33.     public void generate(String source, String target, int width, int height) throws IOException;  
  34.       
  35. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.image.BufferedImage;  
  4. import java.io.IOException;  
  5.   
  6. /** 
  7.  *  
  8.  * BufferedImage 的读写处理策略接口 
  9.  *  
  10.  * @author 梁飞 liangfei0201@163.com 
  11.  * 
  12.  */  
  13. public interface ImageProvider {  
  14.       
  15.     public BufferedImage readImage(String source) throws IOException;  
  16.       
  17.     public void saveImage(BufferedImage image, String target) throws IOException;  
  18.   
  19. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.image.BufferedImage;  
  4. import java.io.BufferedInputStream;  
  5. import java.io.BufferedOutputStream;  
  6. import java.io.FileInputStream;  
  7. import java.io.FileOutputStream;  
  8. import java.io.IOException;  
  9.   
  10. import com.sun.image.codec.jpeg.JPEGCodec;  
  11. import com.sun.image.codec.jpeg.JPEGImageDecoder;  
  12. import com.sun.image.codec.jpeg.JPEGImageEncoder;  
  13.   
  14. /** 
  15.  *  
  16.  * Sun的非标准JPEG处理类,没ImageIO之前,全靠它撑着 
  17.  *  
  18.  * @author 梁飞 liangfei0201@163.com 
  19.  * 
  20.  */  
  21.   
  22. public class CodecImageProvider implements ImageProvider {  
  23.   
  24.     public BufferedImage readImage(String source) throws IOException {  
  25.         BufferedInputStream bis = null;  
  26.         try {  
  27.             bis = new BufferedInputStream(new FileInputStream(source));  
  28.             JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(bis);  
  29.             return decoder.decodeAsBufferedImage();  
  30.         } finally {  
  31.             if (bis != null) {  
  32.                 bis.close();  
  33.             }  
  34.         }  
  35.     }  
  36.   
  37.     public void saveImage(BufferedImage image, String target) throws IOException {  
  38.         BufferedOutputStream bos = null;  
  39.         try {  
  40.             bos = new BufferedOutputStream(new FileOutputStream(target));  
  41.             JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(bos);  
  42.             encoder.encode(image);  
  43.         } finally {  
  44.             if (bos != null) {  
  45.                 bos.close();  
  46.             }  
  47.         }  
  48.     }  
  49.   
  50. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.image.BufferedImage;  
  4. import java.io.File;  
  5. import java.io.IOException;  
  6. import java.util.Iterator;  
  7.   
  8. import javax.imageio.IIOImage;  
  9. import javax.imageio.ImageIO;  
  10. import javax.imageio.ImageTypeSpecifier;  
  11. import javax.imageio.ImageWriteParam;  
  12. import javax.imageio.ImageWriter;  
  13. import javax.imageio.stream.ImageOutputStream;  
  14.   
  15. /** 
  16.  *  
  17.  * 标准的Java实现图形处理包,处理性能待考量 
  18.  *  
  19.  * @author 梁飞 liangfei0201@163.com 
  20.  * 
  21.  */  
  22.   
  23. public class IioImageProvider implements ImageProvider {  
  24.   
  25.     public BufferedImage readImage(String source) throws IOException {  
  26.         return ImageIO.read(new File(source));  
  27.     }  
  28.   
  29.     public void saveImage(BufferedImage image, String target) throws IOException {  
  30.         File targetFile = new File(target);  
  31.         ImageWriter writer = null;  
  32.         ImageOutputStream outputStream = null;  
  33.         try {  
  34.             ImageTypeSpecifier type = ImageTypeSpecifier  
  35.                     .createFromRenderedImage(image);  
  36.             Iterator iterator = ImageIO.getImageWriters(type, "JPEG");  
  37.             if (!iterator.hasNext()) {  
  38.                 return;  
  39.             }  
  40.             writer = (ImageWriter) iterator.next();  
  41.             IIOImage iioImage = new IIOImage(image, nullnull);  
  42.             ImageWriteParam param = writer.getDefaultWriteParam();  
  43.             param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);  
  44.             param.setCompressionQuality(1.0f);  
  45.             outputStream = ImageIO.createImageOutputStream(targetFile);  
  46.             writer.setOutput(outputStream);  
  47.             writer.write(null, iioImage, param);  
  48.         } finally {  
  49.             if (outputStream != null) {  
  50.                 outputStream.close();  
  51.             }  
  52.             if (writer != null) {  
  53.                 writer.abort();  
  54.             }  
  55.         }  
  56.     }  
  57.   
  58. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.Dimension;  
  4. import java.awt.Image;  
  5. import java.awt.Toolkit;  
  6. import java.io.IOException;  
  7.   
  8. /** 
  9.  *  
  10.  * 最原始的方法,Toolkit一般用于桌面加载图片,调用的地方到处是ImageObserver 
  11.  *  
  12.  * @author 梁飞 liangfei0201@163.com 
  13.  * 
  14.  */  
  15. public class AwtPreviewGenerator implements PreviewGenerator {  
  16.   
  17.     public Dimension getSize(String source) throws IOException {  
  18.         Image image = Toolkit.getDefaultToolkit().getImage(source);  
  19.         return new Dimension(image.getWidth(null), image.getHeight(null));  
  20.     }  
  21.   
  22.     public void generate(String source, String target, int width, int height)  
  23.             throws IOException {  
  24.         Image image = Toolkit.getDefaultToolkit().getImage(source);  
  25.         image = image.getScaledInstance(width, height, Image.SCALE_FAST);  
  26.         // TODO Image保存方式待定  
  27.         // imageProvider.saveImage((BufferedImage)image, target);  
  28.     }  
  29.   
  30. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.Color;  
  4. import java.awt.Dimension;  
  5. import java.awt.Graphics2D;  
  6. import java.awt.RenderingHints;  
  7. import java.awt.image.BufferedImage;  
  8. import java.io.IOException;  
  9.   
  10. /** 
  11.  *  
  12.  * 这个是自己操大刀,用Graphics直接画出来,再保存 
  13.  *  
  14.  * 注:重构前,就此类+IioImageProvider的实现方式 
  15.  *  
  16.  * @author 梁飞 liangfei0201@163.com 
  17.  * 
  18.  */  
  19.   
  20. public class J2dPreviewGenerator implements PreviewGenerator {  
  21.       
  22.     private ImageProvider imageProvider;  
  23.       
  24.     public J2dPreviewGenerator(ImageProvider imageProvider) {  
  25.         this.imageProvider = imageProvider;  
  26.     }  
  27.   
  28.     public Dimension getSize(String source) throws IOException {  
  29.         BufferedImage image = imageProvider.readImage(source);  
  30.         return new Dimension(image.getWidth(), image.getHeight());  
  31.     }  
  32.   
  33.     public void generate(String source, String target, int width, int height) throws IOException {  
  34.         BufferedImage sourceImage = imageProvider.readImage(source);  
  35.         BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);  
  36.         Graphics2D g = null;  
  37.         try {  
  38.             g = (Graphics2D) image.createGraphics();  
  39.             g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);  
  40.             g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);  
  41.             // TODO hints待调整 ...  
  42.             g.setColor(Color.white);  
  43.             g.fillRect(00, width, height);  
  44.             g.drawImage(sourceImage, 00, width, height, null);  
  45.         } finally {  
  46.             if (g != null) {  
  47.                 g.dispose();  
  48.             }  
  49.         }  
  50.         imageProvider.saveImage(image, target);  
  51.     }  
  52.   
  53. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.Dimension;  
  4. import java.awt.RenderingHints;  
  5. import java.awt.geom.AffineTransform;  
  6. import java.awt.image.AffineTransformOp;  
  7. import java.awt.image.BufferedImage;  
  8. import java.io.IOException;  
  9.   
  10. /** 
  11.  *  
  12.  * 标准的Image处理的装饰器模式实现,感觉比直接操Graphic好些 
  13.  *  
  14.  * @author 梁飞 liangfei0201@163.com 
  15.  * 
  16.  */  
  17.   
  18. public class OpPreviewGenerator implements PreviewGenerator {  
  19.       
  20.     private ImageProvider imageProvider;  
  21.       
  22.     public OpPreviewGenerator(ImageProvider imageProvider) {  
  23.         this.imageProvider = imageProvider;  
  24.     }  
  25.   
  26.     public Dimension getSize(String source) throws IOException {  
  27.         BufferedImage image = imageProvider.readImage(source);  
  28.         return new Dimension(image.getWidth(), image.getHeight());  
  29.     }  
  30.   
  31.     public void generate(String source, String target, int width, int height) throws IOException {  
  32.         BufferedImage sourceImage = imageProvider.readImage(source);  
  33.         AffineTransform affineTransform = new AffineTransform();  
  34.         affineTransform.scale(width, height);  
  35.         RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_DEFAULT);  
  36.         // TODO hints待调整 ...  
  37.         AffineTransformOp affineTransformOp = new AffineTransformOp(affineTransform, hints);  
  38.         BufferedImage image = new BufferedImage(width, height, sourceImage.getType());  
  39.         image = affineTransformOp.filter(sourceImage, image);  
  40.         imageProvider.saveImage(image, target);  
  41.     }  
  42.   
  43. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.Dimension;  
  4. import java.awt.image.renderable.ParameterBlock;  
  5. import java.awt.image.renderable.RenderableImage;  
  6. import java.io.File;  
  7. import java.io.FileOutputStream;  
  8. import java.io.IOException;  
  9.   
  10. import javax.media.jai.JAI;  
  11. import javax.media.jai.ParameterBlockJAI;  
  12. import javax.media.jai.PlanarImage;  
  13.   
  14. import com.sun.media.jai.codec.ImageCodec;  
  15. import com.sun.media.jai.codec.ImageEncoder;  
  16. import com.sun.media.jai.codec.JPEGEncodeParam;  
  17.   
  18. /** 
  19.  *  
  20.  * ImageIO的前身,但保持独立发展,比ImageIO绑定JDK,更新速度快多了 
  21.  *  
  22.  * @author 梁飞 liangfei0201@163.com 
  23.  * 
  24.  */  
  25.   
  26. public class JaiPreviewGenerator implements PreviewGenerator {  
  27.   
  28.     public Dimension getSize(String source) throws IOException {  
  29.         ParameterBlockJAI loadPB = new ParameterBlockJAI("fileload");  
  30.         loadPB.setParameter("filename", source);  
  31.         PlanarImage image = JAI.create("fileload", loadPB);  
  32.         return new Dimension(image.getWidth(), image.getHeight());  
  33.     }  
  34.   
  35.     public void generate(String source, String target, int width, int height) throws IOException {  
  36.         ParameterBlock pb = new ParameterBlock();  
  37.         pb.addSource(source);  
  38.         RenderableImage render = JAI.createRenderable("renderable", pb);  
  39.         PlanarImage image = (PlanarImage) render.createScaledRendering(width, height, null);  
  40.         save(image, target);  
  41.     }  
  42.       
  43.     private void save(PlanarImage image, String target) throws IOException {  
  44.         File targetFile = new File(target);  
  45.         FileOutputStream out = null;  
  46.         try {  
  47.             JPEGEncodeParam param = new JPEGEncodeParam();  
  48.             param.setQuality(1.00f);  
  49.             out = new FileOutputStream(targetFile);  
  50.             ImageEncoder encoder = ImageCodec.createImageEncoder("JPEG", out, param);  
  51.             encoder.encode(image);  
  52.         } finally {  
  53.             if (out != null) {  
  54.                 out.close();  
  55.             }  
  56.         }  
  57.     }  
  58.   
  59. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.Dimension;  
  4. import java.io.IOException;  
  5.   
  6. import magick.CompressionType;  
  7. import magick.ImageInfo;  
  8. import magick.MagickApiException;  
  9. import magick.MagickException;  
  10. import magick.MagickImage;  
  11.   
  12. /** 
  13.  *  
  14.  * Unix下最著名的ImageMagicK库的Java调用 
  15.  * JMagicK并不处理什么,只是ImageMagicK到Java的一个jni接口,其方法大部分是native的 
  16.  *  
  17.  * 注:已放在正式环境下run了 
  18.  *  
  19.  * @author 梁飞 liangfei0201@163.com 
  20.  * 
  21.  */  
  22.   
  23. public class JmagickPreviewGenerator implements PreviewGenerator {  
  24.       
  25.     public JmagickPreviewGenerator() {  
  26.         System.setProperty("jmagick.systemclassloader""no");  
  27.     }  
  28.   
  29.     public Dimension getSize(String source) throws IOException {  
  30.         try {  
  31.             ImageInfo info = new ImageInfo(source);  
  32.             MagickImage image = new MagickImage(info);  
  33.             return image.getDimension();  
  34.         } catch (MagickApiException ex) {  
  35.             throw new IOException(ex.getMessage());  
  36.         } catch (MagickException ex) {  
  37.             throw new IOException(ex.getMessage());  
  38.         }  
  39.     }  
  40.   
  41.     public void generate(String source, String target, int width, int height) throws IOException {  
  42.         try {  
  43.             ImageInfo info = new ImageInfo(source);  
  44.             MagickImage image = new MagickImage(info);  
  45.             MagickImage canvasImage = image.scaleImage(width, height);  
  46.             save(canvasImage, target);  
  47.         } catch (MagickApiException ex) {  
  48.             ex.printStackTrace();  
  49.             throw new IOException(ex.getMessage());  
  50.         } catch (MagickException ex) {  
  51.             ex.printStackTrace();  
  52.             throw new IOException(ex.getMessage());  
  53.         }  
  54.     }  
  55.       
  56.     private void save(MagickImage image, String target) throws IOException {  
  57.         try {  
  58.             image.setCompression(CompressionType.JPEGCompression);  
  59.             image.setFileName(target);  
  60.             image.writeImage(new ImageInfo());  
  61.         } catch (MagickApiException ex) {  
  62.             ex.printStackTrace();  
  63.             throw new IOException(ex.getMessage());  
  64.         } catch (MagickException ex) {  
  65.             ex.printStackTrace();  
  66.             throw new IOException(ex.getMessage());  
  67.         }  
  68.     }  
  69.   
  70. }  


Java代码 复制代码
  1. package com.sanook.hompy.util.image.preview;  
  2.   
  3. import java.awt.Dimension;  
  4. import java.io.IOException;  
  5.   
  6. /** 
  7.  * 在生物方面用的较多,如细胞透视图等的处理 
  8.  *  
  9.  * @author 梁飞 liangfei0201@163.com 
  10.  * 
  11.  */  
  12. public class ImagejPreviewGenerator implements PreviewGenerator {  
  13.   
  14.     public Dimension getSize(String source) throws IOException {  
  15.         // TODO 没写完  
  16.         return null;  
  17.     }  
  18.   
  19.     public void generate(String source, String target, int width, int height) throws IOException {  
  20.         // TODO 没写完  
  21.           
  22.     }  
  23.   
  24. }  


结果:
用Op比直接用Graphic要好些,
JMagicK的内存占有会低很多,
速度也比iio快3到5倍。
但负载还是偏高。

资源:
ImageMagicK: http://www.imagemagick.org/ (提供C/C++原生接口)
JMagicK: http://www.yeo.id.au/jmagick/  (Java接口)
RMagicK: http://rmagick.rubyforge.org/ (Ruby接口)
也支持其它语言:
LISP: http://common-lisp.net/project/cl-magick/
PASCAL: http://wiki.lazarus.freepascal.org/index.html/PascalMagick
PHP: http://www.magickwand.org/download/php/
PYTHON: http://www.imagemagick.org/download/python/
TCL: http://tclmagick.sourceforge.net/

BTW:
ImageMagicK和JMagicK安装很麻烦哦,最好都用源代码包编译安装,rpm包安装常出错。
  • src.rar (8.5 KB)
  • 描述: 源代码包
  • 下载次数: 219
  • lib.rar (3 MB)
  • 描述: 程序依赖包
  • 下载次数: 488
原创粉丝点击