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

java图片处理 博客分类: java基础 java图片处理文件上传 

程序员文章站 2024-03-25 14:52:28
...

java中经常需要上传图片,对图片类型进行校验,常常使用的校验是通过文件后缀或者getContentType来判断文件类型是否符合,但是如果你使用了flash上传,有可能得到的图片类型为:application/octet-stream(任意的二进制数)如果你将这个类型加入到了判断条件里边实际上就没有多大意义了,所以确切的文件类型校验就需要如下判断了。所以可以考虑如下操作。

 

java图片处理类包含如下类:

 

第一:图片类型类:

 

/**
 * 图片类型
 * 
 * @author 
 * @since 2010-5-19
 */
public enum ImageType {

	unknown(0, "unknown"), jpg(1, "jpg"), gif(2, "gif"), png(3, "png"), bmp(4,
			"bmp");

	private int code;
	private String name;

	ImageType(int code, String name) {
		this.code = code;
		this.name = name;
	}

	public int getCode() {
		return code;
	}

	public String getName() {
		return name;
	}

	/**
	 * 将后缀转换成图片类型, JPEG将转成jpg
	 * 
	 * @param suffix
	 * @return
	 */
	public static ImageType toImageType(String suffix) {
		if (suffix == null || "".equals(suffix)) {
			return unknown;
		}
		suffix = suffix.toLowerCase();
		if ("jpeg".equals(suffix)) {
			suffix = "jpg";
		}
		try {
			return valueOf(suffix);
		} catch (Exception e) {
			return unknown;
		}
	}

	/**
	 * 判断图片类型
	 * 
	 * @param suffix
	 * @return
	 */
	public static boolean isAcceptType(String suffix) {
		if (suffix == null || "".equals(suffix)) {
			return false;
		}
		if ("jpeg".equalsIgnoreCase(suffix)) {
			return true;
		}
		ImageType type = ImageType.valueOf(suffix.toLowerCase());
		if (type != null && type.getCode() > 0 && type.getCode() < 5) {
			return true;
		}
		return false;
	}

	public static boolean isAcceptType(ImageType type) {
		if (type == null) {
			return false;
		}
		return isAcceptType(type.getName());
	}
}

 

 图片扩展信息类:

 

import java.io.Serializable;

/**
 * JPG的EXIT信息 ,相关规范参考: 1.* EXIF.org http://www.exif.org/ 2.* Opanda
 * http://www.opanda.com/cn/iexif/exif.htm 3.* EXIF 2.1
 * 官方标准(PDF文档)http://www.exif.org/Exif2-1.PDF 4.* EXIF 2.2
 * 官方标准(PDF文档)http://www.exif.org/Exif2-2.PDF 5.* EXIF 文件格式说明
 * http://park2.wakwak.com/~tsuruzoh/Computer/Digicams/exif-e.html
 * 
 * @author 
 * @since 2010-5-19
 */
public class ImageExif implements Serializable {

	private static final long serialVersionUID = 4713490466591635082L;

	private String ImageDescription;// 图像描述、来源. 指生成图像的工具   
	private String Artist;// 作者 有些相机可以输入使用者的名字   
	private String Make;// 生产者 指产品生产厂家   
	private String Model;// 型号 指设备型号   
	private String Orientation;// 方向 有的相机支持,有的不支持   
	private String XResolution; // X方向分辨率
	private String YResolution;// Y方向分辨率
	private String ResolutionUnit;// 分辨率单位 一般为PPI
	private String Software;// 软件 显示固件
	private String Firmware;// 版本
	private String DateTime;// 日期和时间
	private String YCbCrPositioning;// 色相定位   
	private String ExifOffsetExif;// 信息位置,定义Exif在信息在文件中的写入,有些软件不显示。
	private String ExposureTime;// 曝光时间 即快门速度  
	private String FNumber; // 光圈系数
	private String ExposureProgram;// 曝光程序 指程序式自动曝光的设置,各相机不同,可能是Sutter
	// Priority(快门优先)、Aperture Priority(快门优先)等等。
	private String IsoSpeedRatings;// 感光度
	private String ExifVersion;// Exif版本
	private String DateTimeOriginal;// 创建时间
	private String DateTimeDigitized;// 数字化时间
	private String ComponentsConfiguration;// 图像构造(多指色彩组合方案)
	private String CompressedBitsPerPixel;// (BPP)压缩时每像素色彩位 指压缩程度
	private String ExposureBiasValue;// 曝光补偿。
	private String MaxApertureValue;// 最大光圈
	private String MeteringMode;// 测光方式,平均式测光、*重点测光、点测光等。
	private String Lightsource;// 光源 指白平衡设置
	private String Flash;// 是否使用闪光灯。
	private String FocalLength;// 焦距,一般显示镜头物理焦距,有些软件可以定义一个系数,从而显示相当于35mm相机的焦距
	private String MakerNote;// (User Comment)作者标记、说明、记录
	private String FlashPixVersionFlashPix;// 版本 (个别机型支持)
	private String ColorSpace;// 色域、色彩空间
	private String ExifImageWidth;// (Pixel X Dimension)图像宽度 指横向像素数
	private String ExifImageLength;// (Pixel Y Dimension)图像高度 指纵向像素数
	private String Interoperability;// IFD通用性扩展项定义指针 和TIFF文件相关,具体含义不详
	private String FileSource;// 源文件 Compression压缩比

	public String getImageDescription() {
		return ImageDescription;
	}

	public void setImageDescription(String imageDescription) {
		ImageDescription = imageDescription;
	}

	public String getArtist() {
		return Artist;
	}

	public void setArtist(String artist) {
		Artist = artist;
	}

	public String getMake() {
		return Make;
	}

	public void setMake(String make) {
		Make = make;
	}

	public String getModel() {
		return Model;
	}

	public void setModel(String model) {
		Model = model;
	}

	public String getOrientation() {
		return Orientation;
	}

	public void setOrientation(String orientation) {
		Orientation = orientation;
	}

	public String getXResolution() {
		return XResolution;
	}

	public void setXResolution(String xResolution) {
		XResolution = xResolution;
	}

	public String getYResolution() {
		return YResolution;
	}

	public void setYResolution(String yResolution) {
		YResolution = yResolution;
	}

	public String getResolutionUnit() {
		return ResolutionUnit;
	}

	public void setResolutionUnit(String resolutionUnit) {
		ResolutionUnit = resolutionUnit;
	}

	public String getSoftware() {
		return Software;
	}

	public void setSoftware(String software) {
		Software = software;
	}

	public String getFirmware() {
		return Firmware;
	}

	public void setFirmware(String firmware) {
		Firmware = firmware;
	}

	public String getDateTime() {
		return DateTime;
	}

	public void setDateTime(String dateTime) {
		DateTime = dateTime;
	}

	public String getYCbCrPositioning() {
		return YCbCrPositioning;
	}

	public void setYCbCrPositioning(String yCbCrPositioning) {
		YCbCrPositioning = yCbCrPositioning;
	}

	public String getExifOffsetExif() {
		return ExifOffsetExif;
	}

	public void setExifOffsetExif(String exifOffsetExif) {
		ExifOffsetExif = exifOffsetExif;
	}

	public String getExposureTime() {
		return ExposureTime;
	}

	public void setExposureTime(String exposureTime) {
		ExposureTime = exposureTime;
	}

	public String getFNumber() {
		return FNumber;
	}

	public void setFNumber(String fNumber) {
		FNumber = fNumber;
	}

	public String getExposureProgram() {
		return ExposureProgram;
	}

	public void setExposureProgram(String exposureProgram) {
		ExposureProgram = exposureProgram;
	}

	public String getIsoSpeedRatings() {
		return IsoSpeedRatings;
	}

	public void setIsoSpeedRatings(String isoSpeedRatings) {
		IsoSpeedRatings = isoSpeedRatings;
	}

	public String getExifVersion() {
		return ExifVersion;
	}

	public void setExifVersion(String exifVersion) {
		ExifVersion = exifVersion;
	}

	public String getDateTimeOriginal() {
		return DateTimeOriginal;
	}

	public void setDateTimeOriginal(String dateTimeOriginal) {
		DateTimeOriginal = dateTimeOriginal;
	}

	public String getDateTimeDigitized() {
		return DateTimeDigitized;
	}

	public void setDateTimeDigitized(String dateTimeDigitized) {
		DateTimeDigitized = dateTimeDigitized;
	}

	public String getComponentsConfiguration() {
		return ComponentsConfiguration;
	}

	public void setComponentsConfiguration(String componentsConfiguration) {
		ComponentsConfiguration = componentsConfiguration;
	}

	public String getCompressedBitsPerPixel() {
		return CompressedBitsPerPixel;
	}

	public void setCompressedBitsPerPixel(String compressedBitsPerPixel) {
		CompressedBitsPerPixel = compressedBitsPerPixel;
	}

	public String getExposureBiasValue() {
		return ExposureBiasValue;
	}

	public void setExposureBiasValue(String exposureBiasValue) {
		ExposureBiasValue = exposureBiasValue;
	}

	public String getMaxApertureValue() {
		return MaxApertureValue;
	}

	public void setMaxApertureValue(String maxApertureValue) {
		MaxApertureValue = maxApertureValue;
	}

	public String getMeteringMode() {
		return MeteringMode;
	}

	public void setMeteringMode(String meteringMode) {
		MeteringMode = meteringMode;
	}

	public String getLightsource() {
		return Lightsource;
	}

	public void setLightsource(String lightsource) {
		Lightsource = lightsource;
	}

	public String getFlash() {
		return Flash;
	}

	public void setFlash(String flash) {
		Flash = flash;
	}

	public String getFocalLength() {
		return FocalLength;
	}

	public void setFocalLength(String focalLength) {
		FocalLength = focalLength;
	}

	public String getMakerNote() {
		return MakerNote;
	}

	public void setMakerNote(String makerNote) {
		MakerNote = makerNote;
	}

	public String getFlashPixVersionFlashPix() {
		return FlashPixVersionFlashPix;
	}

	public void setFlashPixVersionFlashPix(String flashPixVersionFlashPix) {
		FlashPixVersionFlashPix = flashPixVersionFlashPix;
	}

	public String getColorSpace() {
		return ColorSpace;
	}

	public void setColorSpace(String colorSpace) {
		ColorSpace = colorSpace;
	}

	public String getExifImageWidth() {
		return ExifImageWidth;
	}

	public void setExifImageWidth(String exifImageWidth) {
		ExifImageWidth = exifImageWidth;
	}

	public String getExifImageLength() {
		return ExifImageLength;
	}

	public void setExifImageLength(String exifImageLength) {
		ExifImageLength = exifImageLength;
	}

	public String getInteroperability() {
		return Interoperability;
	}

	public void setInteroperability(String interoperability) {
		Interoperability = interoperability;
	}

	public String getFileSource() {
		return FileSource;
	}

	public void setFileSource(String fileSource) {
		FileSource = fileSource;
	}

}

 图片文件对象:

import java.io.File;
import java.io.Serializable;

/**
 * 图片文件信息
 * 
 * @author 
 * @since 2010-5-19
 */
public class ImageFile implements Serializable {

	private static final long serialVersionUID = -337911125594555523L;

	/**
	 * 图片宽,单位:px
	 */
	private double width;

	/**
	 * 图片高,单位:px
	 */
	private double height;

	/**
	 * 图片大小,单位:byte
	 */
	private double size;

	/**
	 * 图片类型
	 */
	private ImageType type;

	/**
	 * 图片文件
	 */
	private File file;

	/**
	 * 图片EXIF信息
	 */
	private ImageExif exif;

	public double getWidth() {
		return width;
	}

	public void setWidth(double width) {
		this.width = width;
	}

	public double getHeight() {
		return height;
	}

	public void setHeight(double height) {
		this.height = height;
	}

	public double getSize() {
		return size;
	}

	public void setSize(double size) {
		this.size = size;
	}

	public ImageType getType() {
		return type;
	}

	public void setType(ImageType type) {
		this.type = type;
	}

	public File getFile() {
		return file;
	}

	public void setFile(File file) {
		this.file = file;
	}

	public ImageExif getExif() {
		return exif;
	}

	public void setExif(ImageExif exif) {
		this.exif = exif;
	}

}
 
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.CropImageFilter;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.RenderedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.MemoryCacheImageInputStream;

import magick.MagickImage;

import org.apache.commons.lang.StringUtils;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGEncodeParam;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.sun.imageio.plugins.bmp.BMPImageReader;
import com.sun.imageio.plugins.gif.GIFImageReader;
import com.sun.imageio.plugins.jpeg.JPEGImageReader;
import com.sun.imageio.plugins.png.PNGImageReader;
import com.sun.imageio.plugins.wbmp.WBMPImageReader;


/**
 * 使用 imageio 实现的图片处理工具
 * 
 * @author
 * @since 2010-7-14
 */
public class ImageIOUtil {

	/**
	 * 是否是合法图片
	 * 
	 * @param suffix
	 *            图片文件后缀
	 * @param imageContent
	 *            图片内容
	 * @return
	 */
	public static boolean isImage(byte[] imageContent) {
		return isImage(null, imageContent);
	}

	/**
	 * 是否是合法图片
	 * 
	 * @param imageContent
	 *            图片内容
	 * @return
	 */
	public static boolean isImage(String suffix, byte[] imageContent) {
		if (imageContent == null || imageContent.length == 0) {
			return false;
		}
		Image img = null;
		InputStream is = null;
		try {
			is = new ByteArrayInputStream(imageContent);
			img = ImageIO.read(is);
			if (img == null || img.getWidth(null) <= 0
					|| img.getHeight(null) <= 0) {
				return false;
			}
			return true;
		} catch (Exception e) {
			return false;
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
				}
			}
		}
	}

	/**
	 * 是否是合法图片
	 * 
	 * @param imageFullPath
	 *            图片本地绝对路径
	 * @return
	 */
	public static boolean isImage(String localImagePath) {
		File imgFile = new File(localImagePath);
		if (!imgFile.isFile()) {
			return false;
		}
		Image img;
		try {
			img = ImageIO.read(imgFile);
			if (imgFile.length() == 0 || img == null || img.getWidth(null) <= 0
					|| img.getHeight(null) <= 0) {
				return false;
			}
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 根据要求的坐标截取图片
	 * 
	 * @param source
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 */
	public static void cropPart(String imageFullPath, int x, int y, int width,
			int height) throws RuntimeException {
		Image img = null;
		ImageFilter cropFilter = null;
		BufferedImage bi = null;
		try {
			bi = ImageIO.read(new File(imageFullPath));
			if (bi == null) {
				throw new RuntimeException("ImageIO.read return null");
			}
		} catch (Exception e) {
			throw new RuntimeException(String.format("read image fail, src=",
					imageFullPath));
		}
		int srcW = bi.getWidth();
		int srcH = bi.getHeight();
		if (srcW <= 0 || srcH <= 0) {
			throw new RuntimeException(String.format("invalid image, src=",
					imageFullPath));
		}
		// 异常的图片参数
		if (x >= srcW || y >= srcH) {
			throw new RuntimeException(
					String
							.format(
									"cropPart fail, point (x=%s,y=%s) not in the image(width=%s,height=%s)",
									x, y, srcW, srcH));
		}
		width = Math.min(width, srcW - x);
		height = Math.min(height, srcH - y);
		try {
			Image image = bi.getScaledInstance(srcW, srcH, Image.SCALE_DEFAULT);
			cropFilter = new CropImageFilter(x, y, width, height);
			img = Toolkit.getDefaultToolkit().createImage(
					new FilteredImageSource(image.getSource(), cropFilter));
			BufferedImage tag = new BufferedImage(width, height,
					BufferedImage.TYPE_INT_RGB);
			Graphics2D g = (Graphics2D) tag.getGraphics();
			g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
					RenderingHints.VALUE_INTERPOLATION_BILINEAR);
			g.drawImage(img, 0, 0, null);
			g.dispose();
			ImageIO.write(tag, "jpg", new File(imageFullPath));
		} catch (Exception e) {
			throw new RuntimeException("process image error, src="
					+ imageFullPath, e);
		}
	}

	/**
	 * 将imageFullPath指定的图片进行等比缩放,最长的边为<t>maxEdgeLength</t>
	 * 
	 * @param imageFullPath
	 *            :需要裁剪的图片绝对路径
	 * @param maxEdgeLength
	 *            : 边长
	 * @return
	 */
	public static boolean resizeImage(String imageFullPath, int maxEdgeLength)
			throws Exception {
		File file = new File(imageFullPath);
		if (!file.exists()) {
			return false;
		}
		Image img = ImageIO.read(file);
		// 判断图片格式是否正确
		if (img == null || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) {
			return false;
		}
		int width = img.getWidth(null);
		int height = img.getHeight(null);

		boolean isWidthLonger = width > height ? true : false;

		// 得到调整后的尺寸及缩小的比例,如果{width,height}都小于等于maxEdgeLength,直接返回
		if (width > maxEdgeLength || height > maxEdgeLength) {
			double ratio;

			if (isWidthLonger) {
				ratio = ((double) maxEdgeLength) / width;
				width = maxEdgeLength;
				height = ((Double) Math.floor(ratio * height)).intValue();
			} else {
				ratio = ((double) maxEdgeLength) / height;
				width = ((Double) Math.floor(ratio * width)).intValue();
				height = maxEdgeLength;
			}
		} else {
			return true;
		}
		FileOutputStream out = null;
		try {
			BufferedImage tag = new BufferedImage((int) width, (int) height,
					BufferedImage.TYPE_INT_RGB);
			tag.getGraphics().drawImage(
					img.getScaledInstance(width, height, Image.SCALE_SMOOTH),
					0, 0, null);
			out = new FileOutputStream(imageFullPath);
			ImageIO.write(tag, "jpg", out);
		} catch (Exception e) {
			throw new RuntimeException("resize image error, img="
					+ imageFullPath, e);
		} finally {
			if (out != null) {
				out.close();
			}
		}
		return true;
	}

	/**
	 * 对imageFullPath 指定的文件按要求的质量quality进行压缩(gif将不会进行压缩)。quality的范围是(0-100)
	 * 
	 * @param imageFullPath
	 *            文件的绝对路径
	 * @param quality
	 *            压缩的质量,范围是(0-100)
	 * @param maxFileSize
	 *            文件超过该大小才进行质量有损压缩,单位:byte
	 * @return 文件大小,单位:byte
	 */
	public static int compressImage(String imageFullPath, int quality,
			long maxFileSize) {
		// 1. entry validation
		if (StringUtil.isEmpty(imageFullPath) || quality < 0 || quality > 100) {
			throw new RuntimeException("invalid parameters, src="
					+ imageFullPath + ",quality=" + quality);
		}
		File img = new File(imageFullPath);
		if (!img.isFile()) {
			throw new RuntimeException("file not exists, src=" + imageFullPath);
		}
		if (img.length() <= maxFileSize) {
			return (int) img.length();
		}

		// 2. compress
		FileOutputStream out = null;
		try {
			// Retrieve jpg image to be compressed
			RenderedImage rendImage = ImageIO.read(new File(imageFullPath));
			if (rendImage == null || rendImage.getWidth() <= 0
					|| rendImage.getHeight() <= 0) {
				throw new RuntimeException("file is not an image, src="
						+ imageFullPath);
			}
			out = new FileOutputStream(img);
			BufferedImage tag = new BufferedImage(rendImage.getWidth(),
					rendImage.getHeight(), BufferedImage.TYPE_INT_RGB);
			tag.getGraphics().drawImage((Image) rendImage, 0, 0,
					rendImage.getWidth(), rendImage.getHeight(), null);
			JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
			JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(tag);
			jep.setQuality(quality / 100f, true);
			encoder.encode(tag, jep);
		} catch (Exception e) {
			throw new RuntimeException("compressImage fail, src="
					+ imageFullPath, e);
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
				}
			}
		}
		return (int) new File(imageFullPath).length();
	}

	/**
	 * 获取图片信息,包括宽/高/大小/类型,如果取不到会抛异常
	 * 
	 * @param imageFullPath
	 * @return
	 * @throws Exception
	 */
	public static ImageFile getImageInfo(String imageFullPath) throws Exception {
		return getImageInfo(imageFullPath, false);
	}

	/**
	 * (本方法暂不支持)获取图片信息+EXIF信息,如果非图片格式会抛异常
	 * 
	 * @param localImagePath
	 *            本地图片路径
	 * @param isReadExif
	 *            是否需要读取exif信息
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public static ImageFile getImageInfo(String localImagePath,
			boolean isReadExif) {
		File imgFile = new File(localImagePath);
		if (!imgFile.isFile()) {
			throw new RuntimeException("file not exists or not a file, file="
					+ localImagePath);
		}
		Image img;
		try {
			img = ImageIO.read(imgFile);
			if (imgFile.length() == 0 || img == null || img.getWidth(null) <= 0
					|| img.getHeight(null) <= 0) {
				throw new RuntimeException(
						"get image's size/width/height error, file="
								+ localImagePath);
			}
		} catch (IOException e) {
			throw new RuntimeException(
					"get image's size/width/height error, file="
							+ localImagePath);
		}
		ImageFile imageFile = new ImageFile();
		imageFile.setWidth(img.getWidth(null));
		imageFile.setHeight(img.getHeight(null));
		imageFile.setSize(imgFile.length());
		imageFile.setFile(imgFile);

		FileInputStream fis = null;
		BufferedInputStream buff = null;
		int leng;
		byte[] mapObj;
		try {
			fis = new FileInputStream(imgFile);
			leng = fis.available();
			buff = new BufferedInputStream(fis);
			mapObj = new byte[leng];
			buff.read(mapObj, 0, leng);
		} catch (IOException e) {
			throw new RuntimeException("read image file stream error, file="
					+ localImagePath);
		}

		String type = null;
		ByteArrayInputStream bais = null;
		MemoryCacheImageInputStream mcis = null;
		try {
			bais = new ByteArrayInputStream(mapObj);
			mcis = new MemoryCacheImageInputStream(bais);
			Iterator itr = ImageIO.getImageReaders(mcis);
			while (itr.hasNext()) {
				ImageReader reader = (ImageReader) itr.next();
				if (reader instanceof GIFImageReader) {
					type = "gif";
				} else if (reader instanceof JPEGImageReader) {
					type = "jpg";
				} else if (reader instanceof PNGImageReader) {
					type = "png";
				} else if (reader instanceof BMPImageReader
						|| reader instanceof WBMPImageReader) {
					type = "bmp";
				}
			}
			if (type != null) {
				imageFile.setType(ImageType.valueOf(type));
				if (isReadExif) {
					// TODO read exif information
				}
				return imageFile;
			}
		} finally {
			if (bais != null) {
				try {
					bais.close();
				} catch (IOException ioe) {
				}
			}
			if (mcis != null) {
				try {
					mcis.close();
				} catch (IOException ioe) {
				}
			}
			if (buff != null) {
				try {
					buff.close();
				} catch (IOException ioe) {
				}
			}
			if (fis != null) {
				try {
					fis.close();
				} catch (IOException ioe) {
				}
			}
		}
		return null;
	}

	private static Method[] exifMethods = ImageExif.class.getMethods();

	private static ImageExif readImageExif(MagickImage image) {
		ImageExif exif = new ImageExif();
		if (image == null) {
			return exif;
		}
		try {
			for (Method method : exifMethods) {
				if ("set".equals(method.getName().substring(0, 3))) {
					if (method.getName().length() > 3) {
						method.invoke(exif, image.getImageAttribute("EXIF:"
								+ method.getName().substring(3)));
					}
				}
			}

		} catch (Exception e) {
			throw new RuntimeException("read image exif error", e);
		}
		return exif;
	}

	/**
	 * 给图片打水印(本地图片会被替换成有水印的图片),如果图片宽高要小于水印宽高+x或y,则不会打水印
	 * 
	 * @param localImage
	 *            源图的本地绝对路径
	 * @param markImage
	 *            水印图片的绝对路径
	 * @param maskType
	 *            0-右下角, 1-左下角, 2-正中间, 3-左上角, 4-右上角, 5-自定义
	 * @param x
	 *            离横向边间隔距离,如左对齐则左边距,右对齐则是右边距,居中传0,自定义则为左边距,单位:px
	 * @param y
	 *            离纵向边距离,如上对齐则上边距,下对齐则是下边距,居中传0,自定义则为上边距,单位:px
	 * @return
	 * @throws Exception
	 */
	public static boolean mask(String localImage, String markImage,
			int maskType, int x, int y) throws Exception {
		return mask(localImage, localImage, markImage, maskType, x, y);
	}

	/**
	 * 给图片打水印(生成目标图片会带水印),如果图片宽高要小于水印宽高+endX或endY,则不会打水印
	 * 
	 * @param imageBytes
	 *            源图的byte数组
	 * @param markImage
	 *            水印图片的绝对路径
	 * @param maskType
	 *            0-右下角, 1-左下角, 2-正中间, 3-左上角, 4-右上角, 5-自定义
	 * @param x
	 *            离横向边间隔距离,如左对齐则左边距,右对齐则是右边距,居中传0,自定义则为左边距,单位:px
	 * @param y
	 *            离纵向边距离,如上对齐则上边距,下对齐则是下边距,居中传0,自定义则为上边距,单位:px
	 * @return 处理后图片的byte数组
	 * @throws Exception
	 */
	public static byte[] mask(byte[] imageBytes, String markImage,
			int maskType, int x, int y) throws Exception {
		File srcTmp = File.createTempFile("ImageIOUtil", null);
		Image src = ImageIO.read(new ByteArrayInputStream(imageBytes));
		Image logo = ImageIO.read(new File(markImage));
		maskCore(src, srcTmp, logo, maskType, x, y);
		InputStream input = null;
		ByteArrayOutputStream out = null;
		try {
			input = new FileInputStream(srcTmp);
			out = new ByteArrayOutputStream(4096);
			byte[] b = new byte[4096];
			int n;
			while ((n = input.read(b)) != -1) {
				out.write(b, 0, n);
				out.flush();
			}
			return out.toByteArray();
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (Exception e) {
				}
			}
			if (out != null) {
				try {
					out.close();
				} catch (Exception e) {
				}
			}
		}
	}

	/**
	 * 给图片打水印(生成目标图片会带水印),如果图片宽高要小于水印宽高+x或y,则不会打水印
	 * 
	 * @param localImage
	 *            源图的本地绝对路径
	 * @param destImage
	 *            目标图片的本地绝对路径
	 * @param markImage
	 *            水印图片的绝对路径
	 * @param maskType
	 *            0-右下角, 1-左下角, 2-正中间, 3-左上角, 4-右上角, 5-自定义
	 * @param x
	 *            离横向边间隔距离,如左对齐则左边距,右对齐则是右边距,居中传0,自定义则为左边距,单位:px
	 * @param y
	 *            离纵向边距离,如上对齐则上边距,下对齐则是下边距,居中传0,自定义则为上边距,单位:px
	 * @return
	 * @throws Exception
	 */
	public static boolean mask(String localImage, String destImage,
			String markImage, int maskType, int x, int y) throws Exception {
		Image src = ImageIO.read(new File(localImage));
		Image logo = ImageIO.read(new File(markImage));
		File dest = new File(destImage);
		return maskCore(src, dest, logo, maskType, x, y);
	}

	/**
	 * 打水印主逻辑
	 * 
	 * @param src
	 *            源图
	 * @param dest
	 *            目标输出文件
	 * @param logo
	 *            水印
	 * @param maskType
	 *            0-右下角, 1-左下角, 2-正中间, 3-左上角, 4-右上角, 5-自定义
	 * @param marginX
	 *            离横向边间隔距离,如左对齐则左边距,右对齐则是右边距,居中传0,自定义则为左边距,单位:px
	 * @param marginY
	 *            离纵向边距离,如上对齐则上边距,下对齐则是下边距,居中传0,自定义则为上边距,单位:px
	 * @return
	 * @throws Exception
	 */
	private static boolean maskCore(Image src, File dest, Image logo,
			int maskType, int marginX, int marginY) throws Exception {
		// 校验图片合法性
		if (src == null || src.getWidth(null) <= 0 || src.getHeight(null) <= 0
				|| dest == null || logo == null || src.getWidth(null) <= 0
				|| src.getHeight(null) <= 0) {
			return false;
		}
		int srcW = src.getWidth(null);
		int srcH = src.getHeight(null);
		int logoW = logo.getWidth(null);
		int logoH = logo.getHeight(null);

		int x = 0, y = 0;
		switch (maskType) {
		// 左下角
		case 1:
			x = marginX;
			y = (int) (srcH - logoH - marginY);
			break;
		// 正中间
		case 2:
			x = (int) ((srcW - logoW) / 2);
			y = (int) ((srcH - logoH) / 2);
			break;
		// 左上角
		case 3:
			x = marginX;
			y = marginY;
			break;
		// 右上角
		case 4:
			x = (int) (srcW - logoW - marginX);
			y = marginY;
			break;
		// 自定义
		case 5:
			x = marginX;
			y = marginY;
			break;
		// 右下角
		case 0:
			// 其它值默认右下角
		default:
			x = (int) (srcW - logoW - marginX);
			y = (int) (srcH - logoH - marginY);
		}

		// 校验水印是否全部落在图片中
		if (x <= 0 || y <= 0 || x > srcW - logoW || y > srcH - logoH) {
			return false;
		}

		FileOutputStream out = null;
		try {
			BufferedImage tag = new BufferedImage((int) srcW, (int) srcH,
					BufferedImage.TYPE_INT_RGB);
			Graphics g = tag.getGraphics();
			g.drawImage(src, 0, 0, srcW, srcH, null);
			g.drawImage(logo, x, y, logoW, logoH, null);
			out = new FileOutputStream(dest);
			ImageIO.write(tag, "jpg", out);
			g.dispose();
		} catch (Exception e) {
			throw new RuntimeException("resize image error", e);
		} finally {
			if (out != null) {
				out.close();
			}
		}
		return true;
	}
}

 上边的图片是要exif是需要使用JMagick获取所以如果有兴趣就需要同学安装,下面给出

JMagickUtil

 

 

import java.awt.Rectangle;
import java.io.File;
import java.lang.reflect.Method;

import magick.CompositeOperator;
import magick.ImageInfo;
import magick.MagickImage;

import org.apache.commons.lang.StringUtils;
import com.taobao.communitypsc.common.image.ImageExif;
import com.taobao.communitypsc.common.image.ImageFile;
import com.taobao.communitypsc.common.image.ImageType;

/**
 * JMagick 处理图片
 * 
 * @author 
 * @since 2010-5-20
 */
public class JMagickUtil {

	static {
		System.setProperty("jmagick.systemclassloader", "no");
	}

	/**
	 * 是否是合法图片
	 * 
	 * @param suffix
	 *            图片文件后缀
	 * @param imageContent
	 *            图片内容
	 * @return
	 */
	public static boolean isImage(String suffix, byte[] imageContent) {
		try {
			MagickImage image = new MagickImage(new ImageInfo(suffix),
					imageContent);
			if (image == null || image.getDimension().getWidth() <= 0) {
				return false;
			}
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * 是否是合法图片
	 * 
	 * @param imageFullPath
	 *            图片本地绝对路径
	 * @return
	 */
	public static boolean isImage(String localImagePath) {
		if (localImagePath == null || !new File(localImagePath).isFile()) {
			return false;
		}
		try {
			MagickImage image = new MagickImage(new ImageInfo(localImagePath));
			if (image.getDimension() == null
					|| image.getDimension().getWidth() <= 0) {
				return false;
			}
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * 根据要求的坐标截取图片
	 * 
	 * @param source
	 * @param x
	 * @param y
	 * @param width
	 * @param height
	 */
	public static void cropPart(String imageFullPath, int x, int y, int width,
			int height) throws Exception {
		MagickImage image = null;
		ImageInfo info = null;
		// 取得原文件
		try {
			info = new ImageInfo(imageFullPath);
			// 获取图片
			image = new MagickImage(info);
			// 原始尺寸
			int beforeScaleX = image.getDimension().width;
			int beforeScaleY = image.getDimension().height;

			// 是否需要这个约束
			int cropWidth = (x + width > beforeScaleX) ? (beforeScaleX - x)
					: width;
			int cropHeight = (y + height > beforeScaleY) ? (beforeScaleY - y)
					: height;

			MagickImage small = image.cropImage(new Rectangle(x, y, cropWidth,
					cropHeight));
			small.setFileName(imageFullPath);
			small.writeImage(new ImageInfo());
			small.destroyImages();
		} finally {
			if (image != null) {
				image.destroyImages();
			}
		}
	}

	/**
	 * 将imageFullPath指定的图片进行等比缩放,最长的边为<t>maxEdgeLength</t>
	 * 
	 * @param imageFullPath
	 *            :需要裁剪的图片绝对路径
	 * @param edgeLength
	 *            : 边长
	 * @return
	 */
	public static boolean resizeImage(String imageFullPath, int maxEdgeLength)
			throws Exception {
		// 取得原文件
		MagickImage image = new MagickImage(new ImageInfo(imageFullPath));
		String suffix = image.getImageFormat();
		if (suffix.equalsIgnoreCase("gif")) {
			image = extractFirstFrame(image);
		}
		// 原始尺寸
		int width = image.getDimension().width;
		int height = image.getDimension().height;

		boolean isWidthLonger = width > height ? true : false;

		// 得到调整后的尺寸及缩小的比例,如果{width,height}都小于等于maxEdgeLength,直接返回
		if (width > maxEdgeLength || height > maxEdgeLength) {
			double ratio;

			if (isWidthLonger) {
				ratio = ((double) maxEdgeLength) / width;
				width = maxEdgeLength;
				height = ((Double) Math.floor(ratio * height)).intValue();
			} else {
				ratio = ((double) maxEdgeLength) / height;
				width = ((Double) Math.floor(ratio * width)).intValue();
				height = maxEdgeLength;
			}

			MagickImage small = image.scaleImage(width, height);
			small.setFileName(imageFullPath);
			small.writeImage(new ImageInfo());
			small.destroyImages();
		}
		return true;
	}

	/**
	 * 截取第一帧
	 * 
	 * @param image
	 *            gif动画
	 * @return
	 */
	private static MagickImage extractFirstFrame(MagickImage image)
			throws Exception {
		MagickImage[] frames = image.breakFrames();
		return frames[0];
	}

	/**
	 * 对imageFullPath 指定的文件按要求的质量quality进行压缩(gif将不会进行压缩)。quality的范围是(0-100)
	 * 
	 * @param imageFullPath
	 *            文件的绝对路径
	 * @param quality
	 *            压缩的质量,范围是(0-100)
	 * @param maxFileSize
	 *            文件超过该大小才进行质量有损压缩,单位:byte
	 * @return 文件大小,单位:byte
	 */
	public static int compressImage(String imageFullPath, int quality,
			long maxFileSize) throws Exception {
		// 1. entry validation
		if (StringUtil.isEmpty(imageFullPath) || quality <= 0 || quality >= 100) {
			return -1;
		}
		int i = imageFullPath.lastIndexOf(".");
		if (i < 0) {
			return -1;
		}

		// 2. compress
		String fileName = imageFullPath.substring(0, i);
		File fileSrc = new File(imageFullPath);

		ImageInfo info = null;
		MagickImage image = null;
		try {
			info = new ImageInfo(imageFullPath);
			image = new MagickImage(info);

			if (null == image || image.getDimension() == null
					|| image.getDimension().getWidth() <= 0) {
				return -1;
			}

			String type = image.getImageFormat();

			if ("gif".equalsIgnoreCase(type)) {
				// 解决 *.giframe 病毒 问题,gif 也通过MagickImage另存一下图片
				fileName = fileName + ".gif";
				image.setFileName(fileName);
				image.writeImage(new ImageInfo());
			} else {
				if (fileSrc.length() > maxFileSize) {// 大于指定文件大小,进行压缩
					// 调整图片品质 最佳为40~50
					info.setQuality(quality);
					image.profileImage("*", null);
					image.setImageAttribute("comment", null);
					image.setImageAttribute("JPEG-Sampling-factors", null);
					image.setFileName(imageFullPath);
					image.writeImage(info);
				}
			}
			return (int) fileSrc.length();
		} finally {
			if (image != null) {
				image.destroyImages();
			}
		}
	}

	/**
	 * 获取图片信息,包括宽/高/大小/类型,如果取不到会抛异常
	 * 
	 * @param imageFullPath
	 * @return
	 * @throws Exception
	 */
	public static ImageFile getImageInfo(String imageFullPath) throws Exception {
		return getImageInfo(imageFullPath, false);
	}

	/**
	 * 获取图片信息+EXIF信息,如果非图片格式会抛异常
	 * 
	 * @param localImagePath
	 *            本地图片路径
	 * @param isReadExif
	 *            是否需要读取exif信息
	 * @return
	 * @throws Exception
	 */
	public static ImageFile getImageInfo(String localImagePath,
			boolean isReadExif) throws Exception {
		File file = new File(localImagePath);
		if (!file.isFile()) {
			throw new Exception("file not exists or not a file, file="
					+ localImagePath);
		}
		ImageFile imageFile = new ImageFile();
		MagickImage image = new MagickImage(new ImageInfo(localImagePath));
		if (image.getDimension() == null
				|| image.getDimension().getWidth() <= 0) {
			throw new Exception("get image's dimension error, file="
					+ localImagePath);
		}
		imageFile.setFile(new File(localImagePath));
		imageFile.setHeight(image.getDimension().getHeight());
		imageFile.setWidth(image.getDimension().getWidth());
		imageFile.setType(ImageType.toImageType(image.getImageFormat()));
		imageFile.setSize(file.length());
		if (isReadExif) {
			imageFile.setExif(readImageExif(image));
		}
		return imageFile;
	}

	private static Method[] exifMethods = ImageExif.class.getMethods();

	private static ImageExif readImageExif(MagickImage image) {
		ImageExif exif = new ImageExif();
		if (image == null) {
			return exif;
		}
		try {
			for (Method method : exifMethods) {
				if ("set".equals(method.getName().substring(0, 3))) {
					if (method.getName().length() > 3) {
						method.invoke(exif, image.getImageAttribute("EXIF:"
								+ method.getName().substring(3)));
					}
				}
			}

		} catch (Exception e) {
			throw new RuntimeException("read image exif error", e);
		}
		return exif;
	}

	/**
	 * 给图片打水印(本地图片会被替换成有水印的图片),如果图片宽高要小于水印宽高+x或y,则不会打水印
	 * 
	 * @param localImage
	 *            源图的本地绝对路径
	 * @param markImage
	 *            水印图片的绝对路径
	 * @param maskType
	 *            0-右下角, 1-左下角, 2-正中间, 3-左上角, 4-右上角, 5-自定义
	 * @param x
	 *            离横向边间隔距离,如左对齐则左边距,右对齐则是右边距,居中传0,自定义则为左边距,单位:px
	 * @param y
	 *            离纵向边距离,如上对齐则上边距,下对齐则是下边距,居中传0,自定义则为上边距,单位:px
	 * @return
	 * @throws Exception
	 */
	public static boolean mask(String localImage, String markImage,
			int maskType, int x, int y) throws Exception {
		return mask(localImage, localImage, markImage, maskType, x, y);
	}

	/**
	 * 给图片打水印(生成目标图片会带水印),如果图片宽高要小于水印宽高+endX或endY,则不会打水印
	 * 
	 * @param imageBytes
	 *            源图的byte数组
	 * @param markImage
	 *            水印图片的绝对路径
	 * @param maskType
	 *            0-右下角, 1-左下角, 2-正中间, 3-左上角, 4-右上角, 5-自定义
	 * @param x
	 *            离横向边间隔距离,如左对齐则左边距,右对齐则是右边距,居中传0,自定义则为左边距,单位:px
	 * @param y
	 *            离纵向边距离,如上对齐则上边距,下对齐则是下边距,居中传0,自定义则为上边距,单位:px
	 * @return 处理后图片的byte数组
	 * @throws Exception
	 */
	public static byte[] mask(byte[] imageBytes, String markImage,
			int maskType, int x, int y) throws Exception {
		ImageInfo info = new ImageInfo();
		MagickImage src = null;
		MagickImage logo = null;
		try {
			src = new MagickImage(info, imageBytes);
			logo = new MagickImage(new ImageInfo(markImage));
			maskCore(src, src, info, logo, maskType, x, y);
			return src.imageToBlob(info);
		} finally {
			if (src != null) {
				src.destroyImages();
			}
			if (logo != null) {
				logo.destroyImages();
			}
		}
	}

	/**
	 * 给图片打水印(生成目标图片会带水印),如果图片宽高要小于水印宽高+x或y,则不会打水印
	 * 
	 * @param localImage
	 *            源图的本地绝对路径
	 * @param destImage
	 *            目标图片的本地绝对路径
	 * @param markImage
	 *            水印图片的绝对路径
	 * @param maskType
	 *            0-右下角, 1-左下角, 2-正中间, 3-左上角, 4-右上角, 5-自定义
	 * @param x
	 *            离横向边间隔距离,如左对齐则左边距,右对齐则是右边距,居中传0,自定义则为左边距,单位:px
	 * @param y
	 *            离纵向边距离,如上对齐则上边距,下对齐则是下边距,居中传0,自定义则为上边距,单位:px
	 * @return
	 * @throws Exception
	 */
	public static boolean mask(String localImage, String destImage,
			String markImage, int maskType, int x, int y) throws Exception {
		MagickImage src = null;
		MagickImage logo = null;
		MagickImage dest = null;
		try {
			ImageInfo info = new ImageInfo(localImage);
			src = new MagickImage(info);
			logo = new MagickImage(new ImageInfo(markImage));
			dest = new MagickImage(info);
			dest.setFileName(destImage);
			return maskCore(src, dest, info, logo, maskType, x, y);
		} finally {
			if (src != null) {
				src.destroyImages();
			}
			if (logo != null) {
				logo.destroyImages();
			}
			if (dest != null) {
				dest.destroyImages();
			}
		}
	}

	/**
	 * 打水印主逻辑
	 * 
	 * @param src
	 *            源图
	 * @param dest
	 *            目标图
	 * @param writeInfo
	 *            目标图的ImageInfo
	 * @param logo
	 *            水印
	 * @param maskType
	 *            0-右下角, 1-左下角, 2-正中间, 3-左上角, 4-右上角, 5-自定义
	 * @param marginX
	 *            离横向边间隔距离,如左对齐则左边距,右对齐则是右边距,居中传0,自定义则为左边距,单位:px
	 * @param marginY
	 *            离纵向边距离,如上对齐则上边距,下对齐则是下边距,居中传0,自定义则为上边距,单位:px
	 * @return
	 * @throws Exception
	 */
	private static boolean maskCore(MagickImage src, MagickImage dest,
			ImageInfo writeInfo, MagickImage logo, int maskType, int marginX,
			int marginY) throws Exception {
		// 校验图片合法性
		if (src == null || src.getDimension() == null || dest == null
				|| dest.getDimension() == null || logo == null
				|| logo.getDimension() == null) {
			return false;
		}
		// gif图片处理,gif多桢不处理,单桢则处理
		String suffix = src.getImageFormat();
		if (suffix.equalsIgnoreCase("gif")) {
			MagickImage[] frames = src.breakFrames();
			if (frames.length > 1) {
				return false;
			}
			src = frames[0];
		}
		double srcW = src.getDimension().getWidth();
		double srcH = src.getDimension().getHeight();
		double logoW = logo.getDimension().getWidth();
		double logoH = logo.getDimension().getHeight();
		if (srcW <= 0 || dest.getDimension().getWidth() <= 0 || logoW <= 0) {
			return false;
		}
		int x = 0, y = 0;

		switch (maskType) {
		// 左下角
		case 1:
			x = marginX;
			y = (int) (srcH - logoH - marginY);
			break;
		// 正中间
		case 2:
			x = (int) ((srcW - logoW) / 2);
			y = (int) ((srcH - logoH) / 2);
			break;
		// 左上角
		case 3:
			x = marginX;
			y = marginY;
			break;
		// 右上角
		case 4:
			x = (int) (srcW - logoW - marginX);
			y = marginY;
			break;
		// 自定义
		case 5:
			x = marginX;
			y = marginY;
			break;
		// 右下角
		case 0:
			// 其它值默认右下角
		default:
			x = (int) (srcW - logoW - marginX);
			y = (int) (srcH - logoH - marginY);
		}

		// 校验水印是否全部落在图片中
		if (x <= 0 || y <= 0 || x > srcW - logoW || y > srcH - logoH) {
			return false;
		}

		dest.compositeImage(CompositeOperator.AtopCompositeOp, logo, x, y);
		dest.writeImage(writeInfo);
		return true;
	}
}

 上传文件为流所以告诉大家一个不常用的好方法:

log4j中有一个StreamUtils.copyThenClose(InputStream, OutputStream);的方法,相信大家都会在自己的项目中使用log4j,所以可以考虑偷个懒试用一下这个类。