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;// 分辨率单位 一般为PPIprivate 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 validationif (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. compressFileOutputStream out = null;try {// Retrieve jpg image to be compressedRenderedImage 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;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;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;}/** * 对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 validationif (StringUtil.isEmpty(imageFullPath) || quality <= 0 || quality >= 100) {return -1;}int i = imageFullPath.lastIndexOf(".");if (i < 0) {return -1;}// 2. compressString 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~50info.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;}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,所以可以考虑偷个懒试用一下这个类。
页:
[1]