springBoot 给图片添加文字水印
项目要求给每个考生制作证书。这里我们有每个公司需要的背景图片模板,而我现在要做的就是把每个考生的考试信息和个人信息,以水印的形式添加到图片的指定位置。(现在这个灵活性不高)下面就直接上代码。
- 首先我这边先写了一个工具类waterMarkUtils 。在这个工具类里面主要设置每个水印的位置以及字体还有存放位置。(我这边因为要上传到服务器,所以就稍微处理了一下,还有我这个可以添加多个水印,图片的来源是网络路径)代码如下:
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
public class WaterMarkUtils {
/**
* @param srcImgPath 源图片路径
* @param tarImgPath 保存的图片路径
* @param waterMarkContent 水印内容
* @param markContentColor 水印颜色
* @param font 水印字体
* @param workerNames
*/
public static Object addWaterMark(String srcImgPath, String tarImgPath, String waterMarkContent, String workerNames,
String workerCardNums, String allStaffOrgNames, BigDecimal studentScores, String certNums, String trainCredentialNames,
String templateContents, Color markContentColor, Font font) {
try {
//new一个url对象
URL url = new URL(srcImgPath);
//打开链接
HttpURLConnection conn = (HttpURLConnection)url.openConnection();
//设置请求方式
/*conn.setRequestMethod("GET");*/
//超时相应时间为5秒
conn.setConnectTimeout(5*1000);
//通过输入流获取图片数据
InputStream inputStream = null;
try {
inputStream = conn.getInputStream();
}catch (Exception e){
e.printStackTrace();
}
//文件转化为图片
Image srcImg = null;
try {
srcImg = ImageIO.read(inputStream);
}catch (Exception e){
e.printStackTrace();
}
//获取图片的宽
int srcImgWidth = srcImg.getWidth(null);
//获取图片的高
int srcImgHeight = srcImg.getHeight(null);
// 加水印
BufferedImage bufImg = new BufferedImage(srcImgWidth, srcImgHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufImg.createGraphics();
g.drawImage(srcImg, 0, 0, srcImgWidth, srcImgHeight, null);
//根据图片的背景设置水印颜色
g.setColor(markContentColor);
//设置字体
g.setFont(font);
//设置时间水印的坐标
int x = (int) (srcImgWidth - 1.64*getWatermarkLength(waterMarkContent, g));
int y = (int) (srcImgHeight - 1.15*getWatermarkLength(waterMarkContent, g));
int x2 = 0;
int y2 = 0;
//设置姓名水印坐标
if (workerNames.length() == 3){
x2 = (int) (srcImgWidth - 7.4*getWatermarkLength(workerNames,g));
y2 = (int) (srcImgHeight - 7.27*getWatermarkLength(workerNames,g));
}else if (workerNames.length() == 2){
x2 = (int) (srcImgWidth - 11*getWatermarkLength(workerNames,g));
y2 = (int) (srcImgHeight - 10.9*getWatermarkLength(workerNames,g));
}else if (workerNames.length() == 4){
x2 = (int) (srcImgWidth - 5.9*getWatermarkLength(workerNames,g));
y2 = (int) (srcImgHeight - 5.45*getWatermarkLength(workerNames,g));
}
int x3 =0;
int y3 =0;
if (workerCardNums.length() == 18){
//设置身份证号水印坐标
x3 = (int) (srcImgWidth - 1.6*getWatermarkLength(workerCardNums,g));
y3 = (int) (srcImgHeight - 0.4*getWatermarkLength(workerCardNums,g));
}else if (workerCardNums.length() == 17){
x3 = (int) (srcImgWidth - 1.7*getWatermarkLength(workerCardNums,g));
y3 = (int) (srcImgHeight - 0.42*getWatermarkLength(workerCardNums,g));
}else if (workerCardNums.length() == 16){
x3 = (int) (srcImgWidth - 1.8*getWatermarkLength(workerCardNums,g));
y3 = (int) (srcImgHeight - 0.45*getWatermarkLength(workerCardNums,g));
}
//设置组织名称水印坐标
int x4 = srcImgWidth - 2*getWatermarkLength(allStaffOrgNames,g);
int y4 = srcImgHeight - 3*getWatermarkLength(allStaffOrgNames,g);
int x5 = 0;
int y5 = 0;
if (studentScores.longValue() >= 100){
//设置分数水印坐标
x5 = (int) (srcImgWidth - 11.8*getWatermarkLength(String.valueOf(studentScores),g));
y5 = (int) (srcImgHeight - 9.7*getWatermarkLength(String.valueOf(studentScores),g));
}else if (studentScores.longValue() <10 && studentScores.longValue()>= 0){
//设置分数水印坐标
x5 = (int) (srcImgWidth - 35*getWatermarkLength(String.valueOf(studentScores),g));
y5 = (int) (srcImgHeight - 28.99*getWatermarkLength(String.valueOf(studentScores),g));
}else if (studentScores.longValue() <= 99 && studentScores.longValue()> 10){
//设置分数水印坐标
x5 = (int) (srcImgWidth - 17.4*getWatermarkLength(String.valueOf(studentScores),g));
y5 = (int) (srcImgHeight - 14.5*getWatermarkLength(String.valueOf(studentScores),g));
}
//设置证书编号水印坐标
int x6 = 0;
int y6 = 0;
if (certNums.length() == 15){
x6 = (int) (srcImgWidth - 2.09*getWatermarkLength(certNums,g));
y6 = (int) (srcImgHeight - 4.14*getWatermarkLength(certNums,g));
}
//设置证书名称水印坐标
int x7 = srcImgWidth - 3*getWatermarkLength(trainCredentialNames,g);
int y7 = srcImgHeight - 2*getWatermarkLength(trainCredentialNames,g);
//设置模板内容水印坐标
int x8 = srcImgWidth - 1*getWatermarkLength(templateContents,g);
int y8 = srcImgHeight - 1*getWatermarkLength(templateContents,g);
//画出水印
g.drawString(waterMarkContent,x,y);
g.drawString(workerNames,x2,y2);
g.drawString(workerCardNums,x3,y3);
/* g.drawString(allStaffOrgNames,x4,y4);*/
g.drawString(String.valueOf(studentScores),x5,y5);
g.drawString(certNums,x6,y6);
/*g.drawString(trainCredentialNames,x7,y7);
g.drawString(templateContents,x8,y8);*/
g.dispose();
// 输出图片
// FileOutputStream outImgStream = new FileOutputStream(tarImgPath);
// ImageIO.write(bufImg, "jpeg", outImgStream);
System.out.println("添加水印完成");
return bufImg;
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
private byte[] readInputStream(InputStream inputStream) throws IOException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
//创建一个Buffer字符串
byte[] buffer = new byte[1024];
//每次读取的字符串长度,如果为-1,代表全部读取完毕
int len = 0;
//使用一个输入流从buffer里把数据读取出来
while( (len=inputStream.read(buffer)) != -1 ){
//用输出流往buffer里写入数据,中间参数代表从哪个位置开始读,len代表读取的长度
outStream.write(buffer, 0, len);
}
//关闭输入流
inputStream.close();
//把outStream里的数据写入内存
return outStream.toByteArray();
}
public static int getWatermarkLength(String waterMarkContent, Graphics2D g) {
return g.getFontMetrics(g.getFont()).charsWidth(waterMarkContent.toCharArray(), 0, waterMarkContent.length());
}
}
然后就是自己写查询的代码了,我这里是直接在serviceImpl 实现类里面调用的这个工具类 代码如下:
service层接口:
serviceImpl实现类:
/**
* 获取证书需要的字段
* @return
*/
@Override
public Map<String,Object> getAllStaffTrainWorker(JSONObject jsonParam) {
String allStaffWorkerId = jsonParam.getString("allStaffWorkerIds");
if (null == allStaffWorkerId){
errLog("getAllStaffTrainWorker", ErrorCodeMsg.PARAM_ERROR.getMessage(), "员工id不存在");
throw new AttemptException(ErrorCodeMsg.PARAM_ERROR,"员工id不存在");
}
//员工表查询出员工的基本信息
Example example = new Example(AllStaffTrainWorker.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("isDeleted", CmnConstants.IS_DELETED_NO);
criteria.andEqualTo("isEnabled", CmnConstants.IS_ENABLED_YES);
String [] allStaffWorkerArry = allStaffWorkerId.split(",");
List<Long> allStaffWorkerIds = new ArrayList<>();
for (String allStaffWorkerIdss :allStaffWorkerArry){
allStaffWorkerIds.add(Long.valueOf(allStaffWorkerIdss));
}
criteria.andIn("allStaffWorkerId",allStaffWorkerIds);
List<AllStaffTrainWorker> allStaffTrainWorkerList = this.allStaffTrainWorkerMapper.selectByExample(example);
//姓名
String workerName = null;
//身份证号
String workerCardNum = null;
//组织名称
String allStaffOrgName = null;
//学员得分
BigDecimal studentScore = null;
//证书编号
String certNum = null;
//证书名称
String trainCredentialName = null;
//获取模板内容
String templateContent = null;
//获取背景图片路径
String imgUrl =null;
if (null != allStaffTrainWorkerList){
for (AllStaffTrainWorker allStaffTrainWorker1 : allStaffTrainWorkerList){
workerName= allStaffTrainWorker1.getWorkerName();
workerCardNum = allStaffTrainWorker1.getWorkerCardNum();
allStaffOrgName = allStaffTrainWorker1.getAllStaffOrgName();
}
//考试表获取分数
Example example1 = new Example(AllStaffTrainWorkerExam.class);
Example.Criteria criteria1 = example1.createCriteria();
criteria1.andEqualTo("isDeleted", CmnConstants.IS_DELETED_NO);
criteria1.andEqualTo("isEnabled", CmnConstants.IS_ENABLED_YES);
criteria1.andEqualTo("allStaffWorkerId",allStaffWorkerId);
List<AllStaffTrainWorkerExam> allStaffTrainWorkerExamList = this.allStaffTrainWorkerExamMapper.selectByExample(example1);
for (AllStaffTrainWorkerExam allStaffTrainWorkerExam : allStaffTrainWorkerExamList){
studentScore = allStaffTrainWorkerExam.getStudentScore();
}
//证书表得到证书编号
Example example2 = new Example(AllStaffTrainWorkerCert.class);
Example.Criteria criteria2 = example2.createCriteria();
criteria2.andEqualTo("isDeleted", CmnConstants.IS_DELETED_NO);
criteria2.andEqualTo("isEnabled", CmnConstants.IS_ENABLED_YES);
criteria2.andEqualTo("allStaffWorkerId",allStaffWorkerId);
List<AllStaffTrainWorkerCert> allStaffTrainWorkerCertList = this.allStaffTrainWorkerCertMapper.selectByExample(example2);
Long allStaffTrainCertId = null;
for (AllStaffTrainWorkerCert allStaffTrainWorkerCert : allStaffTrainWorkerCertList){
certNum = allStaffTrainWorkerCert.getCertNum();
allStaffTrainCertId = allStaffTrainWorkerCert.getAllStaffTrainCertId();
}
//配置证书表获取证书名称
Example example3= new Example(AllStaffTrainCert.class);
Example.Criteria criteria3 = example3.createCriteria();
criteria3.andEqualTo("isDeleted", CmnConstants.IS_DELETED_NO);
criteria3.andEqualTo("isEnabled", CmnConstants.IS_ENABLED_YES);
criteria3.andEqualTo("allStaffTrainCertId",allStaffTrainCertId);
List<AllStaffTrainCert> allStaffTrainCertList = this.allStaffTrainCertMapper.selectByExample(example3);
//模板id
Long allStaffTrainTemplateId = null;
//图片id
Long allStaffTrainImgId = null;
for (AllStaffTrainCert allStaffTrainCert : allStaffTrainCertList){
trainCredentialName = allStaffTrainCert.getTrainCredentialName();
allStaffTrainTemplateId = allStaffTrainCert.getAllStaffTrainTemplateId();
allStaffTrainImgId = allStaffTrainCert.getAllStaffTrainImgId();
}
Example example4 = new Example(AllStaffTrainCertTemplate.class);
Example.Criteria criteria4 = example4.createCriteria();
criteria4.andEqualTo("isDeleted", CmnConstants.IS_DELETED_NO);
criteria4.andEqualTo("isEnabled", CmnConstants.IS_ENABLED_YES);
criteria4.andEqualTo("allStaffTrainTemplateId",allStaffTrainTemplateId);
AllStaffTrainCertTemplate allStaffTrainCertTemplate = this.allStaffTrainCertTemplateMapper.selectOneByExample(example4);
//获取模板内容
templateContent = allStaffTrainCertTemplate.getTemplateContent();
Example example5 = new Example(AllStaffTrainCertImg.class);
Example.Criteria criteria5 = example5.createCriteria();
criteria5.andEqualTo("isDeleted", CmnConstants.IS_DELETED_NO);
criteria5.andEqualTo("isEnabled", CmnConstants.IS_ENABLED_YES);
criteria5.andEqualTo("allStaffTrainImgId",allStaffTrainImgId);
AllStaffTrainCertImg allStaffTrainCertImg = this.allStaffTrainCertImgMapper.selectOneByExample(example5);
//获取背景图片路径
imgUrl = allStaffTrainCertImg.getImgUrl();
}
ClassPathResource classPathResource = new ClassPathResource("fonts/msyh.ttc");
InputStream inputStream = null;
try {
inputStream = classPathResource.getInputStream();
}catch (Exception e){
e.getMessage();
}
Font dynamicFont = null;
try {
dynamicFont = Font.createFont(Font.TRUETYPE_FONT, inputStream);
}catch (Exception e){
e.printStackTrace();
}
Font font = dynamicFont.deriveFont(Font.PLAIN, 28); //水印字体 方法3
//水印字体
/*Font font = new Font("微软雅黑", Font.PLAIN, 28);*/
//源图片地址
String srcImgPath="http://"+imgUrl;
//待存储的地址
String tarImgPath = "F:/证书.jpeg";
//格式化时间
SimpleDateFormat f=new SimpleDateFormat("yyyy MM dd");
String format = f.format(new Date());
//时间
String waterMarkContent=format;
//姓名
String workerNames = workerName;
//身份证号
String workerCardNums = workerCardNum;
//组织名称
String allStaffOrgNames = allStaffOrgName;
//学员得分
BigDecimal studentScores = studentScore;
//证书编号
String certNums =certNum;
//证书名称
String trainCredentialNames = trainCredentialName;
//获取模板内容
String templateContents = templateContent;
//水印图片色彩以及透明度
Color color=new Color(79 ,79 ,79);
BufferedImage bufferedImage = (BufferedImage) WaterMarkUtils.addWaterMark(srcImgPath, tarImgPath, waterMarkContent, workerNames, workerCardNums, allStaffOrgNames, studentScores, certNums, trainCredentialNames, templateContents, color, font);
String sessionToke =jsonParam.getString("sessionToke");
String attname = "证书"+".jpeg";
String url = null;
try {
byte[] jpegs = imageToBytes(bufferedImage, "jpeg");
url = FastDFSUtils.getDoUpload(sessionToke,attname,jpegs);
}catch (Exception e){
errLog("getDownloadAllStaffWorkers",ErrorCodeMsg.UNKNOWN_ERROR.getMessage(),e.getMessage());
throw new AttemptException(ErrorCodeMsg.UNKNOWN_ERROR,e.getMessage());
}
Map<String, Object> resultMap = getFastDFSFileInfoMap(url,attname);
return resultMap;
}
public byte[] imageToBytes(BufferedImage bImage, String format) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(bImage, format, out);
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
/**
* 返回给前端下载地址
*
* @param url
* @param attname
* @return
*/
private Map<String, Object> getFastDFSFileInfoMap(String url, String attname) {
String filePath = null;
if (StringUtils.isNotEmpty(url)) {
JSONObject jsonObject = JSONObject.parseObject(url);
String data = jsonObject.getString("data");
JSONObject dataObject = JSONObject.parseObject(data);
filePath = dataObject.getString("filePath");
if (StringUtils.isEmpty(filePath)) {
errLog("getFastDFSFileInfoMap", ErrorCodeMsg.UNKNOWN_ERROR.getMessage(), "获取文件服务下载地址为空");
throw new AttemptException(ErrorCodeMsg.UNKNOWN_ERROR, "获取文件服务下载地址为空");
}
}
Map<String, Object> map = new HashMap<>(32);
map.put("url",fileServer+"/"+filePath);
map.put("attname", attname);
return map;
}
controller层代码
/**
* 证书需要的内容
* @return
*/
@PostMapping("/getAllStaffTrainWorker")
public ResponseStruct getAllStaffTrainWorker(@RequestBody JSONObject jsonParam){
return ResponseStruct.success(this.allStaffTrainWorkerCertInterface.getAllStaffTrainWorker(jsonParam));
}
总结:
这里面的坑:
1.图片是我们这边上传到服务器的所以数据库存放的的 url 而且输入到浏览器可以直接下载的。所以读取文件url的时候,我进行了了处理 具体代码就在工具类最上面
2.每个水印因为文字的字体和大小变动,位置也会变动
3.刚一开始文字字体用的Windows自带的字体,上传到服务器之后,因为服务器没有字体。要么下载一个要么想我这样 把字体包放到项目根目录然后再用流方式获取。
4.字体包位置