java 给图片打水印 Plus
之前写了一篇文章,java 给图片打水印,虽然呢,功能是实现了,但是,代码结构混乱、效率低下,还使用到了SUN包下的东西,到服务器Maven打包的时候都打包不了,部门老大看了都看不下去了,忍不住亲自操刀斧正了一番,斧正后的就称呼plus版吧,我测试了下效率,原版是200+ms一张图片,plus版是10+ms,实在是汗颜啊。。。。。。。这里把plus版的贴出来,以后敲代码多借鉴下面的代码思想
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.List;
import javax.imageio.ImageIO;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ttpai.cms.content.model.ImageDeal;
import com.ttpai.cms.content.model.ImageGujia;
import com.ttpai.tfs.TFSClicent;
/**
* 估价文章图片,画图工具类
* 1、不变的做缓存,字体和背景图 。用到的字体size不多可以考虑做缓存,因为Font.deriveFont会再new一个Font,可以减少gc
* 2、图片上传改为直接TFS上传,不往磁盘上写,不但增加IO还很慢
* 3、增加异常处理。如:a)TFS上传异常 b)绘图异常。则返回NULL ,为什么返回空字符串? 区分NULL和“”的含义。
* 4、图片限制最大宽高:1200F, 900F。图片已知大小,去掉多余的调用
* 5、不要用main写测试用例
* 6、根据开闭原则,多考虑给外部提供接口有哪些,其他接口一律屏蔽。
* @author rickycoder
*
*/
public class AppraiseImageUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(AppraiseImageUtils.class);
private static final String IMAGE_URL = "http://pic001.ttpaicdn.com/images/";
private static final String PRICE_UNIT = "万元";// 估价的数字和单位是不同的size,所以需要另外绘画
/**
* 车况很新背景图
*/
private static BufferedImage backgroundImageNew = null;
/**
* 车况良好背景图
*/
private static BufferedImage backgroundImageGood = null;
/**
* 车况一般背景图
*/
private static BufferedImage backgroundImageGeneral = null;
/**
* 成交背景图
*/
private static BufferedImage backgroundImageDeal = null;
private static Font fontBold28 = null;
private static Font fontBold36 = null;
private static Font fontRegular18 = null;
private static Font fontRegular20 = null;
static {
try {
backgroundImageNew =
ImageIO.read(AppraiseImageUtils.class
.getResourceAsStream("/appraise/appraise_background_new.png"));
backgroundImageGood =
ImageIO.read(AppraiseImageUtils.class
.getResourceAsStream("/appraise/appraise_background_good.png"));
backgroundImageGeneral =
ImageIO.read(AppraiseImageUtils.class
.getResourceAsStream("/appraise/appraise_background_general.png"));
backgroundImageDeal =
ImageIO.read(AppraiseImageUtils.class
.getResourceAsStream("/appraise/background_deal.png"));
} catch (IOException e) {
LOGGER.error("加载背景图报错", e);
}
InputStream is = null;
BufferedInputStream bis = null;
Font definedFont = null;
try {
is = AppraiseImageUtils.class.getResourceAsStream("/font/PingFang_Bold.ttf");
bis = new BufferedInputStream(is);
definedFont = Font.createFont(Font.TRUETYPE_FONT, bis);
fontBold28 = definedFont.deriveFont(28f);
fontBold36 = definedFont.deriveFont(36f);
is = AppraiseImageUtils.class.getResourceAsStream("/font/PingFang_SC_Regular.otf");
bis = new BufferedInputStream(is);
definedFont = Font.createFont(Font.TRUETYPE_FONT, bis);
fontRegular18 = definedFont.deriveFont(18f);
fontRegular20 = definedFont.deriveFont(20f);
is.close();
bis.close();
} catch (FontFormatException e) {
LOGGER.error("字体格式报错", e);
} catch (IOException e) {
LOGGER.error("加载字体格式报错", e);
} finally {
is = null;
bis = null;
}
definedFont = null;
}
private AppraiseImageUtils() {}
/**
* @Description: 上传图片到TFS
* @param out 图片数据流
* @return String
* @by: yan.zhang@2017年12月15日
*/
private static String uploadImage(ByteArrayOutputStream out) {
if (null == out || 0 == out.size()) {
return null;
}
// 设置图片上传至服务器的配置并上传
TFSClicent tfsClicent = new TFSClicent();
String address = null;
try {
address = IMAGE_URL + tfsClicent.uploadImage(out.toByteArray());
} catch (Exception e) {
LOGGER.error("图片上传失败", e);
}
return address;
}
/**
* @Description:填充文字到图片上(成交列表)
* @param inImagePath
* @param outImagePath
* @param deals
* @return String 返回TFS上的图片地址
* @by: yan.zhang@2017年12月14日
*/
public static String createDealImage(List<ImageDeal> deals) {
Image image = backgroundImageDeal;
int width = image.getWidth(null);
int height = image.getHeight(null);
BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bImage.createGraphics();
g.setColor(Color.black);
g.setBackground(Color.white);
/* 画出图片 */
g.drawImage(image, 0, 0, null);
Color color = new Color(102, 102, 102);
/** 下面代码的参数都是根据UED的图片用尺子量的,不会产生魔数,请不要codeReview **/
int x1 = 75;
int x2 = 248;
int x3 = 427;
int x4 = 630;
int y = 90;// 递增48
for (int i = 0; i < deals.size(); i++) {
ImageDeal deal = deals.get(i);
addWord2image(g, deal.getDealCity(), x1, y, fontRegular18, color);// 成交城市
addWord2image(g, DateUtils.formatDateS(deal.getCardTime()), x2, y, fontRegular18, color);// 上牌时间
addWord2image(g, DateUtils.formatDateS(deal.getDealTime()), x3, y, fontRegular18, color);// 成交时间
if (deal.getDealPrice().length() == 6) {// 若是99.99万,则坐标向左挪19个单位
addWord2image(g, deal.getDealPrice(), x4 - 35, y, fontRegular18, color);// 成交价格
} else if (deal.getDealPrice().length() == 5) {// 若是99.9万,则坐标向左挪14个单位
addWord2image(g, deal.getDealPrice(), x4 - 25, y, fontRegular18, color);// 成交价格
} else if (deal.getDealPrice().length() == 4) {// 若是9.9万,则坐标向左挪44个单位
addWord2image(g, deal.getDealPrice(), x4 - 7, y, fontRegular18, color);// 成交价格
} else if (deal.getDealPrice().length() == 3) {// 若是99万,则坐标向左挪44个单位
addWord2image(g, deal.getDealPrice(), x4 - 10, y, fontRegular18, color);// 成交价格
} else {
addWord2image(g, deal.getDealPrice(), x4, y, fontRegular18, color);// 成交价格
}
y += 48;
}
g.dispose();// 画笔结束
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(bImage, "jpg", out);
String imageUrl = uploadImage(out);
out.close();
return imageUrl;
} catch (Exception e) {
LOGGER.error("createDealImage" + e.getMessage(), e);
}
return null;
}
/**
* 创建车况很新的估价图
*
* @param vo
* @return
*/
public static String createGujiaNewImage(ImageGujia vo) {
return createGujiaImage(backgroundImageNew, vo);
}
/**
* 创建车况良好的估价图
*
* @param vo
* @return
*/
public static String createGujiaGoodImage(ImageGujia vo) {
return createGujiaImage(backgroundImageGood, vo);
}
/**
* 创建车况一般的估价图
*
* @param vo
* @return
*/
public static String createGujiaGeneralImage(ImageGujia vo) {
return createGujiaImage(backgroundImageGeneral, vo);
}
/**
* @Description:填充文字到图片上(估价页)
* @param backgroundImage 背景图
* @param vo 图片参数实体
* @return String 返回TFS上的图片地址
* @by: yan.zhang@2017年12月14日
*/
private static String createGujiaImage(BufferedImage backgroundImage, ImageGujia vo) {
Image image = backgroundImage;
int width = image.getWidth(null);
int height = image.getHeight(null);
BufferedImage bImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bImage.createGraphics();
g.setColor(Color.black);
g.setBackground(Color.white);
/* 画出图片 */
g.drawImage(image, 0, 0, null);
/** 下面代码的参数都是根据UED的图片用尺子量的,不会产生魔数,请不要codeReview **/
Color color = new Color(51, 51, 51);// 大标题颜色
if (vo.getTitle().length() <= 32) {// 标题字小于32,则用planA
addWord2image(g, vo.getTitle(), 10, 62, fontBold28, color);// 标题
} else {
String titleLine1 = vo.getTitle().substring(0, 32);
addWord2image(g, titleLine1, 10, 41, fontBold28, color);// 标题
String titleLine2 = vo.getTitle().substring(32);
addWord2image(g, titleLine2, 10, 78, fontBold28, color);// 标题
}
color = new Color(81, 81, 81);// 行2文字的颜色 20 35
addWord2image(g, vo.getCity(), 80, 120, fontRegular20, color);// 城市
addWord2image(g, vo.getCarAge(), 200, 120, fontRegular20, color);// 车龄
addWord2image(g, vo.getMileAge(), 310, 120, fontRegular20, color);// 里程
color = new Color(253, 96, 63);// 估价和估价单位的颜色
addWord2image(g, vo.getGujia(), 60, 243, fontBold36, color);// 估价
addWord2image(g, PRICE_UNIT, 156, 243, fontRegular20,color);//估价的单位,估价的数字和单位是不同的size,所以需要另外绘画
color = new Color(167, 167, 167);// 指导价的颜色
addWord2image(g, vo.getGuidancePrice(), 327, 243, fontRegular20, color);// 指导价
g.dispose();// 画笔结束
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(bImage, "jpg", out);
String imageUrl = uploadImage(out);
out.close();
return imageUrl;
} catch (Exception e) {
LOGGER.error("createGujiaImage" + e.getMessage(), e);
}
return null;
}
/**
* @Description: 把文字绘画到图片上,绘画内容为空就不画了。
* @param g Graphics2D
* @param addWord 要加上去的文字
* @param x 横坐标
* @param y 纵坐标
* @param font 字的类型
* @param color 字的颜色 void
* @by: yan.zhang@2017年12月14日
*/
private static void addWord2image(Graphics2D g, String addWord, int x, int y, Font font,
Color color) {
// 绘画内容为空就不画了。
if (StringUtils.isBlank(addWord)) {
return;
}
AttributedString ats = new AttributedString(addWord);
g.setFont(font);
g.setColor(color);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
ats.addAttribute(TextAttribute.FONT, font, 0, addWord.length());
AttributedCharacterIterator iter = ats.getIterator();
g.drawString(iter, x, y);
}
}