需求是这样子的:
当用户在我们的公众号里购买课程成功后,我要推送一条课程详情的信息和一张图片;图片里有购买课程的用户头像,用户昵称,课程名称,讲师的二维码等,由于这些信息都是动的,那我只能用图片的组合关系来做。
下面是最终效果图和UI提供的模板图,四个地方是动的(用户头像,用户昵称,课程名称,讲师的二维码)。UI出个图,把不动的地方给写死,然后标出动的地方的坐标值(x多少px,y多少px),图给到我,我把用户头像,昵称等信息依次填到该坐标上。
最终效果图
UI提供的模板图
下面贴代码
package com.ywwl.nmedia.api.uulijun_pic_test;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import com.ywwl.common.util.JedisUtil;
import com.ywwl.nmedia.api.biz.base.SpringApplicationContextUtil;
import com.ywwl.nmedia.api.biz.util.OssFileUtil;
import org.apache.commons.lang3.StringUtils;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
/**
* @Author whb
* @Date 21:30 2018/7/9
**/
public class ImageUtil {
//模板图片(存放在阿里云的OSS上)
private static final String ORIGINAL_IMG_PATH = "lesson/qrcode/2018/07/89670065122466.jpg";
//图片传到服务器里的路径
private static final String LECTURER_QRCODE_PATH = "/opt/lectuereQrcode.jpg";
//private static final String LECTURER_QRCODE_PATH = "/Users/whb/Documents/lectuereQrcode.jpg";
/**OssFileUtil.getUploadFilePath这个方法是我们公司自己写的,不方便贴出来,其实作用就是,把图片路径传进来,返回完整地址
伪代码是 String http://xxx.com:8080/lesson/qrcode/123.jpg = OssFileUtil.getUploadFilePath(String lesson/qrcode/123.jpg);
确保 http://xxx.com:8080/lesson/qrcode/123.jpg 在浏览器里输入后能看到图片
OssFileUtil.uploadFile(InputStream in, uploadPathReal) 这个就是把图片上传到OSS里
*/
/**
* 绘制图片 * * @param headImg 用户头像(完整地址) * http://thirdwx.qlogo.cn/mmopen/vi_32/UUIiaLFI3nrye6cCG7oEcur40FmlBOINm0G3fYId9yzoDSk41v04zBd8kDPxf7j82wc325P237R7RScCcwR1TFg/132 * @param nickname 用户昵称 * @param lessonName 课程名称 * @param qrcode 讲师二维码 * @param cover 课程封面图 lesson/icon/2018/01/359819524709343.jpg * @return 绘制后的图片在OSS里的路径
*/
public String drawImg(String headImg, String nickname, String lessonName, String qrcode, String cover) {
BufferedInputStream is = null;
OutputStream os = null;
String key = "writePicToPath";
InputStream in = null;
JedisUtil jedisUtil = SpringApplicationContextUtil.getBean(JedisUtil.class);
try {
String path = OssFileUtil.getUploadFilePath(ORIGINAL_IMG_PATH);//获取模板图片完整地址
is = new BufferedInputStream(readFile(path));//读取到模板图片流
JPEGImageDecoder jpegDecoder = JPEGCodec.createJPEGDecoder(is);//通过JPEG图像流创建JPEG数据流解码器
BufferedImage buffImg = jpegDecoder.decodeAsBufferedImage();//解码当前JPEG数据流,返回BufferedImage对象
Graphics g = buffImg.getGraphics();//得到画笔对象
BufferedImage imgCover = ImageUtil.getBufferedImage(OssFileUtil.getUploadFilePath(cover), "lesson/icon/", 341 * 2, 255 * 2);
g.drawImage(imgCover, 0 + 30 + 4, 0 + 30 + 3, null);//将封面图绘制到模板图上
g.setColor(Color.BLACK);
BufferedImage img = ImageUtil.roundImg(headImg);//把头像裁剪成圆形
g.drawImage(img, 146 * 2 + 30 + 5, 229 * 2 + 30 + 5, null);//将头像绘制到模板图上
g.setColor(Color.BLACK);
Font f = new Font("宋体", Font.PLAIN, 30);
Color myColor = Color.BLACK;
g.setColor(myColor);
g.setFont(f);
FontMetrics fmNick = g.getFontMetrics(f);//昵称居中设置
int nickWidth = fmNick.stringWidth(nickname);
int nickWidthX = (730 - nickWidth) / 2;
g.drawString(nickname, nickWidthX, 285 * 2 + 30 + 30); //绘制文字
FontMetrics fmLesson = g.getFontMetrics(f);//课程名称居中设置
// 课程名称较长,还要进行换行处理,设置每14个字换行
int batch = 14;
int line = lessonName.length() / batch;
if (lessonName.length() % batch != 0) line++;
int begin = 0;
int end = 14;
int h = 0;//上下行间距30px
String lessonNameTmp = "";
boolean isEnd = false;
for (int i = 0; i < line; i++) {
if (lessonName.length() <= 14) {
//课程名称少于14个字
int lessonWidth = fmLesson.stringWidth(lessonName);
int lessonWidthX = (730 - lessonWidth) / 2;
g.drawString(lessonNameTmp, lessonWidthX, 342 * 2 + 30 + 20 + 5 + 5);
break;
}
lessonNameTmp = lessonName.substring(begin, end);//每次截取14个字,把文字绘制到模板图上
int lessonWidth = fmLesson.stringWidth(lessonNameTmp);
int lessonWidthX = (730 - lessonWidth) / 2;
g.drawString(lessonNameTmp, lessonWidthX, 342 * 2 + 30 + 20 + 5 + 5 + h);
h += 30;
begin += batch;
end += batch;
if (isEnd) break;
if (end > lessonName.length()) {
end = lessonName.length();
isEnd = true;
}
}
BufferedImage imgQrcode = ImageUtil.getBufferedImage(OssFileUtil.getUploadFilePath(qrcode), "lesson/qrcode/", 90 * 2 + 10, 90 * 2 + 10);
g.drawImage(imgQrcode, 126 * 2 + 30 + 5, 448 * 2 + 30 + 5, null);//将讲师二维码绘制到模板图上
g.setColor(Color.BLACK);
g.dispose(); //把绘制好的图片写到下面这个图片里,加锁处理
if (!lock(jedisUtil, key)) return null;
os = new FileOutputStream(LECTURER_QRCODE_PATH);
JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os);//创建编码器,用于编码内存中的图像数据
en.encode(buffImg); //写完之后,再次读到这个图片
in = new BufferedInputStream(new FileInputStream(new File(LECTURER_QRCODE_PATH)));
String uploadPathReal = OssFileUtil.getUploadPath("lesson/qrcode", ".jpg"); //把最终图片上传到OSS里
OssFileUtil.uploadFile(in, uploadPathReal); //返回最终图片上传到OSS里的路径
return uploadPathReal;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
if (os != null) {
os.close();
}
if (is != null) {
is.close();
}
delLock(jedisUtil, key);
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private boolean lock(JedisUtil jedisUtil, String key) {
if (jedisUtil.setnx(key, "1", 1 * 10) <= 0) {
return false;
}
return true;
}
private boolean delLock(JedisUtil jedisUtil, String key) {
if (jedisUtil.del(key) > 0) {
return true;
}
return false;
}
/**
* 获取图片流 * * @param fileUrl http://url:port/store/lesson/qrcode/2018/07/12909064255321.jpg * @return InputStream
*/
public static InputStream readFile(String fileUrl) {
if (StringUtils.isBlank(fileUrl)) {
return null;
}
try {
URL url = new URL(fileUrl);
HttpURLConnection urlCon = (HttpURLConnection) url.openConnection();
urlCon.setConnectTimeout(6000);
urlCon.setReadTimeout(6000);
int code = urlCon.getResponseCode();
if (code != HttpURLConnection.HTTP_OK) {
throw new Exception("文件读取失败");
}
return urlCon.getInputStream();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 改变图片尺寸
*
* @param imgUrl 图片的路径 domail + path
* @param width 修改后的宽度
* @param height 修改后的高度
* @return 并返回BufferedImage对象
*/
public static BufferedImage getBufferedImage(String imgUrl, String path, int width, int height) {
if (StringUtils.isBlank(imgUrl)) {
return null;
}
try {
BufferedImage bi = ImageIO.read(new URL(imgUrl));
BufferedImage tag = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
tag.getGraphics().drawImage(bi, 0, 0, width, height, null);
ByteArrayOutputStream bs = new ByteArrayOutputStream();
ImageOutputStream imOut = ImageIO.createImageOutputStream(bs);
ImageIO.write(tag, "jpg", imOut);
InputStream is = new ByteArrayInputStream(bs.toByteArray());
JPEGImageDecoder jpegDecoder = JPEGCodec.createJPEGDecoder(is);
BufferedImage buffImg = jpegDecoder.decodeAsBufferedImage();
return buffImg;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 把头像裁剪成圆形
*
* @param headImg 图片的完整地址 domail + path
* @return 并返回BufferedImage对象
*/
public static BufferedImage roundImg(String headImg) {
if (StringUtils.isBlank(headImg)) {
return null;
}
try {
BufferedImage bi1 = ImageIO.read(new URL(headImg));
BufferedImage bi2 = new BufferedImage(bi1.getWidth(), bi1.getHeight(), BufferedImage.TYPE_INT_RGB);
Ellipse2D.Double shape = new Ellipse2D.Double(0, 0, bi1.getWidth(), bi1.getHeight());
Graphics2D g2 = bi2.createGraphics();
g2.setBackground(Color.WHITE);
g2.fill(new Rectangle(bi2.getWidth(), bi2.getHeight()));
g2.setClip(shape); // 使用 setRenderingHint 设置抗锯齿
g2.drawImage(bi1, 0, 0, null);
g2.dispose();
ByteArrayOutputStream bs = new ByteArrayOutputStream();
ImageOutputStream imOut = ImageIO.createImageOutputStream(bs);
ImageIO.write(bi2, "jpg", imOut);
InputStream is = new ByteArrayInputStream(bs.toByteArray());
JPEGImageDecoder jpegDecoder = JPEGCodec.createJPEGDecoder(is);
BufferedImage buffImg = jpegDecoder.decodeAsBufferedImage();
return buffImg;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
}
因为 import com.sun.image.codec.jpeg.JPEGCodec;
这个在jdk1.7还是1.6以后就不建议使用了,如果我们要用的话,就要在
pom文件里配置下,打包的时候把rt.jar包打进去
<build>
<filters>
<filter>../build/${env}.properties</filter>
</filters>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.0.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<!-- 解决jdk1.6以上不识别JPEGCodec问题-->
<compilerArguments>
<verbose />
<bootclasspath>${java.home}/lib/rt.jar;${java.home}/lib/jce.jar</bootclasspath>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.3.6.v20151106</version>
<configuration>
<scanIntervalSeconds>0</scanIntervalSeconds>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
这样就可以了,但是会有两个问题:
1、本身jdk就不建议使用,用起来不优雅
2、打包的时候要把rt.jar打进来,导致东西变多,打包变慢
所以我还在寻找更好的方法,把上面的给替换掉
下一篇我会对本篇文章的代码进行优化