com.github.dennisit.vplus.data.support.imagemagic.ImgWrapper Maven / Gradle / Ivy
package com.github.dennisit.vplus.data.support.imagemagic;
import com.github.dennisit.vplus.data.utils.FileWriteUtils;
import lombok.Data;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* 图片转换的操作类 (裁剪+旋转+伸缩+水印+边框+)
* Created by yihui on 16/11/2.
*/
public class ImgWrapper {
public static Builder of(String file) {
checkForNull(file, "Cannot specify null for input file.");
if (file.startsWith("http")) {
throw new IllegalArgumentException("file should not be URI resources! file: " + file);
}
return Builder.ofString(file);
}
public static Builder of(URI uri) {
checkForNull(uri, "Cannot specify null for input uri.");
return Builder.ofUrl(uri);
}
public static Builder of(InputStream inputStream) {
checkForNull(inputStream, "Cannot specify null for InputStream.");
return Builder.ofStream(inputStream);
}
private static void checkForNull(Object o, String message) {
if (o == null) {
throw new NullPointerException(message);
}
}
public static class Builder {
private T sourceFile;
/**
* 图片类型 JPEG, PNG, GIF ...
*
* 默认为jpg图片
*/
private String outputFormat = "jpg";
private List operates = new ArrayList<>();
public Builder(T sourceFile) {
this.sourceFile = sourceFile;
}
private static Builder ofString(String str) {
return new Builder(ImgWrapper.class.getClassLoader().getResource(str).getFile());
}
private static Builder ofUrl(URI url) {
return new Builder(url);
}
private static Builder ofStream(InputStream stream) {
return new Builder(stream);
}
/**
* 设置输出的文件格式
*
* @param format 格式化
* @return 结果模型
*/
public Builder setOutputFormat(String format) {
this.outputFormat = format;
return this;
}
private void updateOutputFormat(String originType) {
if (this.outputFormat != null || originType == null) {
return;
}
int index = originType.lastIndexOf(".");
if (index <= 0) {
return;
}
this.outputFormat = originType.substring(index + 1);
}
public Builder scale(Integer width, Integer height, Integer quality) {
return scale(width, height, quality, false);
}
public Builder scale(Integer width, Integer height, Integer quality, boolean forceScale) {
Operate operate = new Operate();
operate.setOperateType(OperateType.SCALE);
operate.setWidth(width);
operate.setHeight(height);
operate.setQuality(quality);
operate.setForceScale(forceScale);
operates.add(operate);
return this;
}
/**
* 按照比例进行缩放
*
* @param radio 1.0 表示不缩放, 0.5 缩放为一半
* @param quality 质量
* @return 结果模型
*/
public Builder scale(Double radio, Integer quality) {
Operate operate = new Operate();
operate.setOperateType(OperateType.SCALE);
operate.setRadio(radio);
operate.setQuality(quality);
operates.add(operate);
return this;
}
/**
* 裁剪
*
* @param x 起始坐标
* @param y 起始坐标
* @param width 宽
* @param height 高
* @return 结果模型
*/
public Builder crop(int x, int y, int width, int height) {
Operate operate = new Operate();
operate.setOperateType(OperateType.CROP);
operate.setWidth(width);
operate.setHeight(height);
operate.setX(x);
operate.setY(y);
operates.add(operate);
return this;
}
/**
* 旋转
*
* @param rotate 角度
* @return 结果模型
*/
public Builder rotate(double rotate) {
Operate operate = new Operate();
operate.setOperateType(OperateType.ROTATE);
operate.setRotate(rotate);
operates.add(operate);
return this;
}
/**
* 上下翻转
*
* @return 结果模型
*/
public Builder flip() {
Operate operate = new Operate();
operate.setOperateType(OperateType.FLIP);
operates.add(operate);
return this;
}
/**
* 左右翻转,即镜像
*
* @return 结果模型
*/
public Builder flop() {
Operate operate = new Operate();
operate.setOperateType(OperateType.FLOP);
operates.add(operate);
return this;
}
/**
* 添加边框
*
* @param width 边框的宽
* @param height 边框的高
* @param color 边框的填充色
* @return 结果模型
*/
public Builder board(Integer width, Integer height, String color) {
Operate args = new Operate();
args.setOperateType(OperateType.BOARD);
args.setWidth(width);
args.setHeight(height);
args.setColor(color);
operates.add(args);
return this;
}
/**
* 添加水印
*
* @param water 水印的源图片 (默认为png格式)
* @param x 添加到目标图片的x坐标
* @param y 添加到目标图片的y坐标
* @param 泛型支持
* @return 结果模型
*/
public Builder water(U water, int x, int y) {
return water(water, "png", x, y);
}
/**
* 添加水印
*
* @param water 水印
* @param imgType 水印图片的类型; 当传入的为inputStream时, 此参数才有意义
* @param x 添加到目标图片的x坐标
* @param y 添加到目标图片的y坐标
* @param 泛型支持
* @return 结果模型
*/
public Builder water(U water, String imgType, int x, int y) {
Operate operate = new Operate();
operate.setOperateType(OperateType.WATER);
operate.setX(x);
operate.setY(y);
operate.setWater(water);
operate.setWaterImgType(imgType);
operates.add(operate);
return this;
}
/**
* 执行图片处理, 并保存文件为: 源文件_out.jpg (类型由输出的图片类型决定)
*
* @return 保存的文件名
* @throws Exception 异常
*/
public String toFile() throws Exception {
return toFile(null);
}
/**
* 执行图片处理,并将结果保存为指定文件名的file
*
* @param outputFilename 若为null, 则输出文件为 源文件_out.jpg 这种格式
* @return 保存的文件名
* @throws Exception 异常
*/
public String toFile(String outputFilename) throws Exception {
if (CollectionUtils.isEmpty(operates)) {
throw new ImgOperateException("operates null!");
}
/**
* 获取原始的图片信息, 并构建输出文件名
* 1. 远程图片,则保存到临时目录下
* 2. stream, 保存到临时目录下
* 3. 本地文件
*
* 输出文件都放在临时文件夹内,和原文件同名,加一个_out进行区分
**/
FileWriteUtils.FileInfo sourceFile = createFile();
if (outputFilename == null) {
outputFilename = FileWriteUtils.getTmpPath() + "/"
+ sourceFile.getFilename() + "_"
+ System.currentTimeMillis() + "_out." + outputFormat;
}
// 执行图片的操作
if (ImgBaseOperate.operate(operates, sourceFile.getAbsFile(), outputFilename)) {
return outputFilename;
} else {
return null;
}
}
/**
* 执行图片操作,并输出字节流
*
* @return 输入流
* @throws Exception 异常抛出
*/
public InputStream asStream() throws Exception {
if (CollectionUtils.isEmpty(operates)) {
throw new ImgOperateException("operate null!");
}
String outputFilename = this.toFile();
if (StringUtils.isBlank(outputFilename)) {
return null;
}
return new FileInputStream(new File(outputFilename));
}
public byte[] asBytes() throws Exception {
if (CollectionUtils.isEmpty(operates)) {
throw new ImgOperateException("operate null!");
}
String outputFilename = this.toFile();
if (StringUtils.isBlank(outputFilename)) {
return null;
}
return BytesTool.file2bytes(outputFilename);
}
public BufferedImage asImg() throws Exception {
if (CollectionUtils.isEmpty(operates)) {
throw new ImgOperateException("operate null!");
}
String outputFilename = this.toFile();
if (StringUtils.isBlank(outputFilename)) {
return null;
}
return ImageIO.read(new File(outputFilename));
}
private FileWriteUtils.FileInfo createFile() throws Exception {
if (this.sourceFile instanceof String) {
/** 生成的文件在源文件目录下 */
updateOutputFormat((String) this.sourceFile);
} else if (this.sourceFile instanceof URI) {
/** 源文件和生成的文件都保存在临时目录下 */
String urlPath = ((URI) this.sourceFile).getPath();
updateOutputFormat(urlPath);
}
return FileWriteUtils.saveFile(this.sourceFile, outputFormat);
}
@Data
public static class Operate {
/**
* 操作类型
*/
private OperateType operateType;
/**
* 裁剪宽; 缩放宽
*/
private Integer width;
/**
* 高
*/
private Integer height;
/**
* 裁剪时,起始 x
*/
private Integer x;
/**
* 裁剪时,起始y
*/
private Integer y;
/**
* 旋转角度
*/
private Double rotate;
/**
* 按照整体的缩放参数, 1 表示不变, 和裁剪一起使用
*/
private Double radio;
/**
* 图片精度, 1 - 100
*/
private Integer quality;
/**
* 颜色 (添加边框中的颜色; 去除图片中某颜色)
*/
private String color;
/**
* 水印图片, 可以为图片名, uri, 或者inputstream
*/
private T water;
/**
* 水印图片的类型
*/
private String waterImgType;
/**
* 强制按照给定的参数进行压缩
*/
private boolean forceScale;
public boolean valid() {
switch (operateType) {
case CROP:
return width != null && height != null && x != null && y != null;
case SCALE:
return width != null || height != null || radio != null;
case ROTATE:
return rotate != null;
case WATER:
// 暂时不支持水印操作
return water != null;
case BOARD:
if (width == null) {
width = 3;
}
if (height == null) {
height = 3;
}
if (color == null) {
color = "#ffffff";
}
case FLIP:
case FLOP:
return true;
default:
return false;
}
}
public String getWaterFilename() throws ImgOperateException {
try {
return FileWriteUtils.saveFile(water, waterImgType).getAbsFile();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public enum OperateType {
/**
* 裁剪
*/
CROP,
/**
* 缩放
*/
SCALE,
/**
* 旋转
*/
ROTATE,
/**
* 水印
*/
WATER,
/**
* 上下翻转
*/
FLIP,
/**
* 水平翻转
*/
FLOP,
/**
* 添加边框
*/
BOARD;
}
}
}