对图像的缩放可以带来很多好处,比如在载入图像时可以有效减少各方面的压力。 
这里依靠thumbnailator、imgscalr这两个jar包分别实现。 
为了能够粗粒度的控制压缩,定义一个ScalrConfig来控制图像尺寸,图像类型(JPG、BMP。。。。)等。

1. public class ScalrConfig {
2. 	//图片质量
3. 	private Float quality = 1F;
4. 	//图片高度
5. 	private int height = 600;
6. 	//图片宽度
7. 	private int width = 600;
8. 	//使长或宽适应到某一个长度
9. 	private Integer size ;
10. 	//重写format
11. 	private ImageExtensionEnum type = ImageExtensionEnum.JPG;
12. 	//是否重写
13. 	private boolean retype = true;
14. 	//保持横纵比
15. 	private boolean aspectRatio = true;
16. 	//压缩过滤
17. 	private ScalrFilter filter;
18. 
19. 	public ScalrConfig(int size,int allowWidth,
20. 			int allowHeight){
21. 		this.size = size;
22. 		this.filter = new DefautlScalrFiter(allowWidth,allowHeight);
23. 	}
24. 
25. 	public ScalrConfig(int width,int height,
26. 			int allowWidth,int allowHeight){
27. 		this.height = height;
28. 		this.width = width;
29. 		this.filter = new DefautlScalrFiter(allowWidth,allowHeight);
30. 	}
31. 
32. 	class DefautlScalrFiter implements ScalrFilter{
33. 
34. 		private int allowWidth;
35. 
36. 		private int allowHeight;
37.  
38. 		@Override
39. 		public boolean doFilter(int _width, int _height) {
40. 			return allowWidth < _width || allowHeight < _height;
41. 		}
42.  
43. 		public DefautlScalrFiter(int allowWidth, int allowHeight) {
44. 			this.allowWidth = allowWidth;
45. 			this.allowHeight = allowHeight;
46. 		}
47. 	}
48. //省略setter、getter


ImageExtensionEnum用来定义图片格式(GIF不支持。。。。)

1. public enum ImageExtensionEnum {
2. 
3. 	JPG,JPEG,PNG,BMP,GIF;
4. 
5. 	public final static String [] getExtensions(){
6. 		ImageExtensionEnum enums [] = ImageExtensionEnum.values();
7. 		String [] extensions = new String [enums.length];
8. 		for(int i=0;i<enums.length;i++)
9. 			extensions[i] = enums[i].name();
10. 		return extensions;
11. 	}
12. 
13. 	public static boolean isJPG(String extension){
14. 		return JPEG.name().equalsIgnoreCase(extension) || 
15. 				JPG.name().equalsIgnoreCase(extension);
16. 	}
17. 
18. 	public static boolean isSameType(String extension1,String extension2){
19.     	if(extension1 == null || extension2 == null) return false;
20.     	return (extension1.equalsIgnoreCase(extension2))||
21.     			(isJPG(extension1)&&isJPG(extension2))?true : false;
22.     }
23. }


ScalrFilter

用来控制图片是否需要被压缩,如果图片过小,此时缩放是不明智的。

    1. public interface ScalrFilter {
    2. 
    3. 	public boolean doFilter(int _width,int _height);
    4.  
    5. }


    下面是图片压缩:

    1. public interface Thumbnailer {
    2. 
    3. 	/**
    4. 	 * 
    5. 	 * @param is 图片文件流
    6. 	 * @param size 图片大小
    7. 	 * @param config 配置
    8. 	 * @param extension 图片后缀
    9. 	 * @return
    10. 	 * @throws Exception
    11. 	 */
    12. 	List<ThumbnailResult> createThumbnails(File file,
    13. 			ScalrConfig...configs) throws Exception;
    14.  
    15. }


    thumnailResult

    是一个接口,用来控制缩放成文件、写入输出流、返回缩放后的图片类型和大小:

    1. public interface ThumbnailResult {
    2. 
    3. 	File writeToFile(File destFile) throws Exception;
    4. 
    5. 	void writeToStream(OutputStream stream) throws Exception;
    6. 
    7. 	int getWidth() throws Exception;
    8. 
    9. 	int getHeight() throws Exception;
    10. 
    11. 	String getExtension();
    12.  
    13. 
    14. }


    最后读取、判断文件:

    1. public abstract class AbstractThumbaniler implements Thumbnailer{
    2. 
    3. 	@Override
    4. 	public List<ThumbnailResult> createThumbnails(File file,
    5. 			ScalrConfig... configs) throws Exception {
    6. 		if(configs == null || configs.length == 0)
    7. 		{
    8. 			throw new IllegalArgumentException("必须指定一个 ScalrConfig");
    9. 		}	
    10. 		if (file == null)
    11. 		{
    12. 			throw new IllegalArgumentException("文件不能为空");
    13. 		}
    14. 		if (!file.exists())
    15. 		{
    16. 			throw new IllegalArgumentException(file.getAbsolutePath() + "不存在");
    17. 		}
    18. 		String oldExtension = MyStringUtils.getFilenameExtension(file.getName());
    19. 		if(!isSupportedOutputFormat(oldExtension))
    20. 		{
    21. 			throw new IllegalArgumentException("文件不符合格式");
    22. 		}
    23. 		BufferedImage image = ImageIO.read(file);
    24. 		if (image == null)
    25. 		{
    26. 			throw new IllegalArgumentException("文件不是一个真正的图片");
    27. 		}
    28. 		List<ThumbnailResult> results = new ArrayList<ThumbnailResult>();
    29. 		for(ScalrConfig config : configs)
    30. 		{
    31. 			ScalrFilter filter = config.getFilter();
    32. 			if(filter == null || (filter != null && 
    33. 			    filter.doFilter(image.getWidth(), image.getHeight())))
    34. 			 {
    35. 				results.add(createThumbnail(image, config,oldExtension));
    36. 			}
    37. 		}
    38. 		return results;
    39. 	}
    40. 
    41. 	abstract ThumbnailResult createThumbnail(BufferedImage image,
    42. 			ScalrConfig config,String oldExtension) throws Exception;
    43. 
    44. 	protected List<String> getSupportedOutputFormats(){
    45. 
    46. 		String[] formats = ImageIO.getWriterFormatNames();
    47. 		if (formats == null)
    48. 		{
    49. 			return Collections.emptyList();
    50. 		}
    51. 		else
    52. 		{
    53. 			return Arrays.asList(formats);
    54. 		}
    55. 	}
    56. 
    57. 	private boolean isSupportedOutputFormat(String format){
    58. 		for (String supportedFormat : getSupportedOutputFormats())
    59. 		{
    60. 			if (supportedFormat.equals(format))
    61. 			{
    62. 				return true;
    63. 			}
    64. 		}
    65. 
    66. 		return false;
    67. 	}
    68.  
    69. }



    首先是 

    thumbnailator

    ,它的使用非常简单! 

    它能够按照比例缩让、固定尺寸缩放、打水印、旋转和切割等各种的功能,牛逼的是完成这些功能只需要一行代码即可,例如:

    1. Thumbnails.of(new File("q:/user.bmp")).scale(0.25F).toFile(new File("q:/dest.bmmp"));
    2. Thumbnails.of(new File("q:/user.bmp")).size(300, 300).toFile(new File("q:/dest.bmmp"));
    3. Thumbnails.of(new File("q:/user.bmp")).size(300,300).
    4.                        keepAspectRatio(false).toFile(newFile("q:/dest.bmmp"));

    下面是具体的使用:

    1. public class CoobirdThumbnailer extends AbstractThumbaniler {
    2.  
    3. 	@Override
    4. 	ThumbnailResult createThumbnail(BufferedImage image, ScalrConfig config,
    5. 			String oldExtension)throws Exception {
    6. 		int width = image.getWidth();
    7. 		int height = image.getHeight();
    8. 		this.calc(config, width, height);
    9. 		final Builder<BufferedImage> builder = Thumbnails.of(image);
    10. 		builder.size(config.getWidth(), config.getHeight())
    11. 				.keepAspectRatio(config.getAspectRatio());
    12. 		if (config.isRetype())
    13. 		{
    14. 			String newExtension = config.getType().name();
    15. 			if (!ImageExtensionEnum.isSameType(newExtension, oldExtension))
    16. 				oldExtension = newExtension;
    17. 		}
    18. 		builder.outputFormat(oldExtension);
    19. 		if (ImageExtensionEnum.isJPG(oldExtension)
    20. 				&& config.getQuality() != null)
    21. 		{
    22. 			builder.outputQuality(config.getQuality());
    23. 		}
    24. 		return new CoobirdThumbnailResult(builder, oldExtension);
    25. 	}
    26.  
    27. 	private ScalrConfig calc(ScalrConfig config, int _width, int _height) {
    28. 		Integer size = config.getSize();
    29. 		if (size != null) {
    30. 			config.setAspectRatio(false);
    31. 			double sourceRatio = (double) _width / (double) _height;
    32. 			if (_width >= _height)
    33. 			{
    34. 				config.setWidth(size);
    35. 				config.setHeight((int) Math.round(size / sourceRatio));
    36. 			}
    37. 			if (_width < _height)
    38. 			{
    39. 				config.setHeight(size);
    40. 				config.setWidth((int) Math.round(size * sourceRatio));
    41. 			}
    42. 		}
    43. 		return config;
    44. 	}
    45.  
    46. 	private final class CoobirdThumbnailResult implements ThumbnailResult {
    47.  
    48. 		private Builder<BufferedImage> builder;
    49.  
    50. 		private String extension;
    51.  
    52. 		private CoobirdThumbnailResult(Builder<BufferedImage> builder,
    53. 				String extension) {
    54. 			super();
    55. 			this.builder = builder;
    56. 			this.extension = extension;
    57. 		}
    58.  
    59. 		@Override
    60. 		public File writeToFile(File destFile) throws Exception {
    61. 			String fileName = destFile.getName();
    62. 			String _extension = MyStringUtils.getFilenameExtension(fileName);
    63. 			if (!extension.equalsIgnoreCase(_extension))
    64. 			{
    65. 				destFile = new File(destFile.getParent(), fileName.concat(".")
    66. 						.concat(extension));
    67. 			}
    68. 			builder.toFile(destFile);
    69. 			return destFile;
    70. 		}
    71.  
    72. 		@Override
    73. 		public void writeToStream(OutputStream stream) throws Exception {
    74. 			builder.toOutputStream(stream);
    75. 		}
    76.  
    77. 		@Override
    78. 		public int getWidth() throws IOException {
    79. 			return builder.asBufferedImage().getWidth();
    80. 		}
    81.  
    82. 		@Override
    83. 		public int getHeight() throws IOException {
    84. 			return builder.asBufferedImage().getHeight();
    85. 		}
    86.  
    87. 		@Override
    88. 		public String getExtension() {
    89. 			return extension;
    90. 		}
    91. 	}
    92.  
    93. }


    imgscalr,同样拥有图片压缩、裁剪等功能,使用同样非常简单。不过它好像没有输出到文件或者输出到流的功能(也许我没发现),它还支持多线程压缩?,感兴趣的可以看看 

    org.imgscalr.AsyncScalr


    官网上的例子:

    1. BufferedImage thumbnail =
    2.   Scalr.resize(image, Scalr.Method.SPEED, Scalr.Mode.FIT_TO_WIDTH,
    3.                150, 100, Scalr.OP_ANTIALIAS);


    具体使用:

    1. public class ImageScalrThumbnailer extends AbstractThumbaniler{
    2.  
    3. 	@Override
    4. 	ThumbnailResult createThumbnail(BufferedImage image, ScalrConfig config,
    5. 			String oldExtension) throws Exception {
    6. 		Integer size = config.getSize();
    7. 		if(size != null)
    8. 		{
    9. 			image = Scalr.resize(image, Method.QUALITY, size);
    10. 		}
    11. 		else
    12. 		{
    13. 			if(!config.getAspectRatio())
    14. 			{
    15. 				image = Scalr.resize(image, Method.QUALITY,
    16. 						Mode.FIT_EXACT,config.getWidth(),config.getHeight());
    17. 			}
    18. 			else
    19. 			{
    20. 				image = Scalr.resize(image, Method.QUALITY,
    21. 						config.getWidth(),config.getHeight());
    22. 			}
    23. 		}
    24. 		if (config.isRetype())
    25. 		{
    26. 			String newExtension = config.getType().name();
    27. 			if (!ImageExtensionEnum.isSameType(newExtension, oldExtension))
    28. 				oldExtension = newExtension;
    29. 		}
    30. 		return new ImgScalrThumbnailResult(oldExtension,
    31. 				config.getQuality(),image);
    32. 	}
    33. 
    34. 	private final class ImgScalrThumbnailResult implements ThumbnailResult{
    35. 
    36. 		private String extension ;
    37. 
    38. 		private Float quality;
    39. 
    40. 		private BufferedImage image;
    41.  
    42. 		@Override
    43. 		public File writeToFile(File destFile) throws Exception {
    44. 			String filename = destFile.getName();
    45. 			String targetExtension = MyStringUtils.getFilenameExtension(filename);
    46. 			if(!ImageExtensionEnum.isSameType(extension, targetExtension))
    47. 			{
    48. 				destFile = new File(destFile.getParent(),
    49. 						filename.concat(".").concat(extension));
    50. 			}
    51. 			ImageUtils.write(image, extension, quality, destFile);
    52. 			return destFile;
    53. 		}
    54.  
    55. 		@Override
    56. 		public void writeToStream(OutputStream stream) throws Exception {
    57. 			ImageUtils.write(image, extension, quality, stream);
    58. 		}
    59.  
    60. 		@Override
    61. 		public int getWidth() {
    62. 			return image.getWidth();
    63. 		}
    64.  
    65. 		@Override
    66. 		public int getHeight() {
    67. 			return image.getHeight();
    68. 		}
    69.  
    70. 		@Override
    71. 		public String getExtension(){
    72. 			return extension;
    73. 		}
    74.  
    75. 		private ImgScalrThumbnailResult(String extension, Float quality,
    76. 				BufferedImage image) {
    77. 			this.extension = extension;
    78. 			this.quality = quality;
    79. 			this.image = image;
    80. 		}
    81. 
    82. 	}
    83.  
    84.  
    85. }


    ImageUtils用来讲BufferedImage输出到文件或者流,具体如下(来源于Thumbanailator的util):

    1. public final class ImageUtils {
    2. 
    3. 	public static void write(BufferedImage img , String formatName,
    4. 			Float quality,File destFile) throws IOException{
    5. 		Iterator<ImageWriter> writers =
    6. 			ImageIO.getImageWritersByFormatName(formatName);
    7. 		if (!writers.hasNext())
    8. 		{
    9. 			throw new UnsupportedFormatException(
    10. 					formatName,
    11. 					"No suitable ImageWriter found for " + formatName + "."
    12. 			);
    13. 		}
    14. 		ImageWriter writer = writers.next();
    15. 		ImageWriteParam writeParam = writer.getDefaultWriteParam();
    16. 		if (writeParam.canWriteCompressed())
    17. 		{
    18. 			writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    19. 			List<String> supportedFormats =
    20. 				ThumbnailatorUtils.getSupportedOutputFormatTypes(formatName);
    21. 
    22. 			if (!supportedFormats.isEmpty())
    23. 			{
    24. 				writeParam.setCompressionType(supportedFormats.get(0));
    25. 			}
    26. 			if(quality != null)
    27. 			{
    28. 				writeParam.setCompressionQuality(quality);
    29. 			}
    30. 		}
    31. 		ImageOutputStream ios;
    32. 		FileOutputStream fos;
    33. 		fos = new FileOutputStream(destFile);
    34. 		ios = ImageIO.createImageOutputStream(fos);
    35. 		if (ios == null || fos == null)
    36. 		{
    37. 			throw new IOException("Could not open output file.");
    38. 		}
    39. 		if (
    40. 				formatName.equalsIgnoreCase("jpg")
    41. 				|| formatName.equalsIgnoreCase("jpeg")
    42. 				|| formatName.equalsIgnoreCase("bmp")
    43. 		)
    44. 		{
    45. 			int width = img.getWidth();
    46. 			int height = img.getHeight();
    47. 			BufferedImage newImage = new BufferedImage(width, height,
    48. 					BufferedImage.TYPE_INT_RGB);
    49. 			Graphics g = newImage.createGraphics();
    50. 			g.drawImage(img, 0, 0, null);
    51. 			g.dispose();
    52. 			img = newImage;
    53. 		}
    54. 		writer.setOutput(ios);
    55. 		writer.write(null, new IIOImage(img, null, null), writeParam);
    56. 		writer.dispose();
    57. 		ios.close();
    58. 		fos.close();
    59. 	}
    60. 
    61. 	public static void write(BufferedImage img,String formatName,
    62. 			Float quality,OutputStream os) throws IOException{
    63. 		Iterator<ImageWriter> writers =
    64. 			ImageIO.getImageWritersByFormatName(formatName);
    65. 		if (!writers.hasNext())
    66. 		{
    67. 			throw new UnsupportedFormatException(
    68. 					formatName,
    69. 					"No suitable ImageWriter found for " + formatName + "."
    70. 			);
    71. 		}
    72. 		ImageWriter writer = writers.next();
    73. 		ImageWriteParam writeParam = writer.getDefaultWriteParam();
    74. 		if (writeParam.canWriteCompressed())
    75. 		{
    76. 			writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    77. 			List<String> supportedFormats =
    78. 				ThumbnailatorUtils.getSupportedOutputFormatTypes(formatName);
    79. 
    80. 			if (!supportedFormats.isEmpty())
    81. 			{
    82. 				writeParam.setCompressionType(supportedFormats.get(0));
    83. 			}
    84. 			if (quality != null)
    85. 			{
    86. 				writeParam.setCompressionQuality(quality);
    87. 			}
    88. 		}
    89. 		ImageOutputStream ios = ImageIO.createImageOutputStream(os);
    90. 		if (ios == null)
    91. 		{
    92. 			throw new IOException("Could not open OutputStream.");
    93. 		}
    94. 		if (
    95. 				formatName.equalsIgnoreCase("jpg")
    96. 				|| formatName.equalsIgnoreCase("jpeg")
    97. 				|| formatName.equalsIgnoreCase("bmp")
    98. 		)
    99. 		{
    100. 			int width = img.getWidth();
    101. 			int height = img.getHeight();
    102. 			BufferedImage newImage = new BufferedImage(width, height,
    103. 					BufferedImage.TYPE_INT_RGB);
    104. 			Graphics g = newImage.createGraphics();
    105. 			g.drawImage(img, 0, 0, null);
    106. 			g.dispose();
    107. 			img = newImage;
    108. 		}
    109. 		writer.setOutput(ios);
    110. 		writer.write(null, new IIOImage(img, null, null), writeParam);
    111. 		writer.dispose();
    112. 		ios.close();
    113. 	}
    114.  
    115. }


    MyStringUitls主要用来获取文件后缀名: 

    1. public class MyStringUtils {
    2. 
    3. 	private static final char EXTENSION_SEPARATOR = '.';
    4. 
    5. 	private static final String FOLDER_SEPARATOR = "/";
    6. 
    7. 	public static String getFilenameExtension(String path) {
    8. 		if (path == null) {
    9. 			return null;
    10. 		}
    11. 		int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);
    12. 		if (extIndex == -1) {
    13. 			return null;
    14. 		}
    15. 		int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);
    16. 		if (folderIndex > extIndex) {
    17. 			return null;
    18. 		}
    19. 		return path.substring(extIndex + 1);
    20. 	}
    21.  
    22. }


    测试:

    1. public class Test {
    2. 
    3. 	public static void main(String [] args) throws Exception{
    4. 		Thumbnailer thumbnailer = ThumbnailerFactory.getThumbnailer();
    5. 		File toThumbnail = new File("q:/user.bmp");
    6. 		ScalrConfig config = new ScalrConfig(104,105, 160,160);
    7. 		config.setAspectRatio(true);
    8. 		config.setQuality(1F);
    9. 		config.setType(ImageExtensionEnum.BMP);
    10. 		List<ThumbnailResult> results = thumbnailer.
    11. 				createThumbnails(toThumbnail, config);
    12. 		if(!results.isEmpty())
    13. 		{
    14. 			File destFile = new File("q:/target22222.bmp");
    15. 			ThumbnailResult result = results.get(0);
    16. 			result.writeToFile(destFile);
    17. 		}
    18. 	}
    19. 
    20. 
    21. }