`
田智伟
  • 浏览: 203913 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

java图片处理

阅读更多

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)) {
				// 解决 trojan.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,所以可以考虑偷个懒试用一下这个类。

 

0
0
分享到:
评论
3 楼 田智伟 2015-05-14  
278506470 写道
楼主,magick 这个包是你自己写的吗? 怎么找不到?找不到那几个java的类,可否提供。

有个包叫jmagick
2 楼 2047699523 2015-05-11  
1 楼 278506470 2014-04-12  
楼主,magick 这个包是你自己写的吗? 怎么找不到?找不到那几个java的类,可否提供。

相关推荐

Global site tag (gtag.js) - Google Analytics