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,所以可以考虑偷个懒试用一下这个类。