此处使用Freemarker实现的html解析,采用itext7实现的html转换为pdf,并且添加水印,最后采用阿里云OSS存储。
引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.3.0version>
dependency>
<dependency>
<groupId>com.mysqlgroupId>
<artifactId>mysql-connector-jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>com.itextpdfgroupId>
<artifactId>html2pdfartifactId>
<version>4.0.3version>
dependency>
<dependency>
<groupId>com.lowagiegroupId>
<artifactId>itextartifactId>
<version>4.2.1version>
dependency>
<dependency>
<groupId>com.itextpdfgroupId>
<artifactId>itext-asianartifactId>
<version>5.2.0version>
dependency>
<dependency>
<groupId>com.aliyun.ossgroupId>
<artifactId>aliyun-sdk-ossartifactId>
<version>3.5.0version>
dependency>
dependencies>
工具类代码
package com.example.javadatatopdf.utils;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.text.Element;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.*;
import java.util.Map;
@Slf4j
@Component
public class PdfUtil {
@Autowired
private Configuration configuration;
/**
* 获取模板内容
*
* @param templateDirectory 模板文件夹
* @param templateName 模板文件名
* @param paramMap 模板参数
* @return
* @throws Exception
*/
public static String getTemplateContent(String templateDirectory, String templateName, Map<String, Object> paramMap) throws Exception {
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
try {
configuration.setDirectoryForTemplateLoading(new File(templateDirectory));
} catch (Exception e) {
log.error(e.getMessage());
}
Writer out = new StringWriter();
Template template = configuration.getTemplate(templateName, "UTF-8");
template.process(paramMap, out);
out.flush();
out.close();
return out.toString();
}
/**
* HTML 转 PDF
*
* @param content html内容
* @param outPath 输出pdf路径
* @return 是否创建成功
*/
public static boolean html2Pdf(String content, String outPath) {
try {
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setCharset("UTF-8");
FontProvider fontProvider = new FontProvider();
fontProvider.addSystemFonts();
converterProperties.setFontProvider(fontProvider);
HtmlConverter.convertToPdf(content, new FileOutputStream(outPath), converterProperties);
} catch (Exception e) {
log.error("生成模板内容失败,{}", e);
return false;
}
return true;
}
/**
* HTML 转 PDF
*
* @param content html内容
* @return PDF字节数组
*/
public static byte[] html2Pdf(String content) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
ConverterProperties converterProperties = new ConverterProperties();
converterProperties.setCharset("UTF-8");
FontProvider fontProvider = new FontProvider();
fontProvider.addSystemFonts();
converterProperties.setFontProvider(fontProvider);
HtmlConverter.convertToPdf(content, outputStream, converterProperties);
} catch (Exception e) {
log.error("生成 PDF 失败,{}", e);
}
return outputStream.toByteArray();
}
/**
* pdf添加水印
*
* @param waterMarkText 水印文字
* @param pdfFileBytes pdf
*/
public static byte[] addWaterMark(String waterMarkText, byte[] pdfFileBytes) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
// 原PDF文件
PdfReader reader = new PdfReader(pdfFileBytes);
// 输出的PDF文件内容
PdfStamper stamper = new PdfStamper(reader, outputStream);
// 字体 来源于 itext-asian jar包
BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", true);
PdfGState gs = new PdfGState();
// 设置透明度
gs.setFillOpacity(0.2f);
gs.setStrokeOpacity(0.3f);
// 间隔距离
int interval = 30;
// 水印的宽高
int textH = 50;
int textW = 80;
Rectangle pageRect = null;
int totalPage = reader.getNumberOfPages() + 1;
for (int i = 1; i < totalPage; i++) {
pageRect = reader.getPageSizeWithRotation(i);
// 内容上层
PdfContentByte content = stamper.getOverContent(i);
content.beginText();
// 字体添加透明度
content.setGState(gs);
// 添加字体大小等
content.setFontAndSize(baseFont, 18);
for (int height = interval + textH; height < pageRect.getHeight();
height = height + textH * 3) {
for (int width = interval + textW; width < pageRect.getWidth() + textW;
width = width + textW * 2) {
// 添加范围
content.showTextAligned(Element.ALIGN_LEFT
, waterMarkText, width - textW,
height - textH, 50);
}
}
content.endText();
// 关闭
stamper.close();
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return outputStream.toByteArray();
}
}
Service层代码
import com.example.javadatatopdf.entity.PersonIntroduce;
import com.example.javadatatopdf.service.PersonIntroduceService;
import com.example.javadatatopdf.utils.OssService;
import com.example.javadatatopdf.utils.PdfUtil;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import javax.annotation.Resource;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
public class PersonIntroduceServiceImpl implements PersonIntroduceService {
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@Resource
private OssService ossService;
/**
* PDF导出类
*
* @param personIntroduce
*/
public String exPersonIntroduce(PersonIntroduce personIntroduce) throws IOException, TemplateException {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("person", personIntroduce);
Writer out = new StringWriter();
//获取模板地址
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("exPdf.html");
template.process(paramMap, out);
out.flush();
out.close();
String templateContent = out.toString();
String fileName = personIntroduce.getPersonName() + "-个人介绍-" + new SimpleDateFormat("yyyy-MM-dd").format(new Date());
// html转pdf
byte[] resources = PdfUtil.html2Pdf(templateContent);
// 添加水印
byte[] resultResources = PdfUtil.addWaterMark("丁宝可", resources);
// 上传 PDF 文件到 OSS
InputStream inputStream = new ByteArrayInputStream(resultResources);
String ossAddress = ossService.uploadFile2OSS(inputStream, fileName + ".pdf");
inputStream.close();
log.info("阿里云OSS返回地址:" + ossAddress);
return ossAddress;
}
}
Service接口
import com.example.javadatatopdf.entity.PersonIntroduce;
import freemarker.template.TemplateException;
import java.io.IOException;
/**
* @Author csw
* @date 2023/6/1
*/
public interface PersonIntroduceService {
String exPersonIntroduce(PersonIntroduce personIntroduce) throws IOException, TemplateException;
}
实体类
import lombok.Data;
@Data
public class PersonIntroduce {
//名称
private String personName ;
//年龄
private Integer personAge ;
//性格描述
private String personalityDesc;
//性别
private String personGender;
//职业
private String personVocation;
//现居地址
private String address;
//创建时间
private String createTime;
}
Controller层代码
import com.example.javadatatopdf.entity.PersonIntroduce;
import com.example.javadatatopdf.service.PersonIntroduceService;
import freemarker.template.TemplateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
@CrossOrigin(origins = "*")
@RestController
public class PersonIntroduceController {
@Autowired
private PersonIntroduceService personIntroduceService;
@GetMapping("/exPdf")
public String exPdfPersonIntroduce() throws TemplateException, IOException {
PersonIntroduce personIntroduce = new PersonIntroduce();
personIntroduce.setPersonName("张三");
personIntroduce.setAddress("杭州");
personIntroduce.setPersonAge(23);
personIntroduce.setPersonGender("男");
personIntroduce.setPersonalityDesc("这个人有点懒");
personIntroduce.setPersonVocation("Java后端");
return personIntroduceService.exPersonIntroduce(personIntroduce);
}
}
HTML模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<style>
body{
font-family: MiSans;
font-size: 15px;
}
.title{
text-align: center;
}
.content{
margin:0 auto;
width: 400px;
}
.content .text{
text-indent: 2em;
}
.content .datetime{
text-align: right;
}
</style>
</head>
<body>
<div>
<div class="view">
<h2 class="title">自我介绍</h2>
<div class="content">
<p class="text">
大家好,我叫${person.personName},我今年${person.personAge},我是个${person.personGender},
我的职业是${person.personVocation},我目前住在${person.address},我在性格方面${person.personalityDesc}。
</p>
</div>
</div>
</div>
</body>
</html>
阿里云OSS工具类
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
@Component
public class OssService {
@Value("${oss.key}")
private String key;
@Value("${oss.secret}")
private String secret;
@Value("${oss.endpoint}")
private String endpoint;
@Value("${oss.bucketname}")
private String bucketName;
private OSS ossClient;
/**
* 上传到OSS服务器 如果同名文件会覆盖服务器上的
*
* @param instream 文件流
* @param fileName 文件名称 包括后缀名
* @return 出错返回"" ,唯一MD5数字签名
*/
public String uploadFile2OSS(InputStream instream, String fileName) {
// 云账号AccessKey有所有API访问权限,建议遵循阿里云安全最佳实践,创建并使用RAM子账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建。
String accessKeyId = key;
String accessKeySecret = secret;
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
String ret = "";
try {
// 创建上传Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(instream.available());
objectMetadata.setCacheControl("no-cache");
objectMetadata.setHeader("Pragma", "no-cache");
objectMetadata.setContentType(getcontentType(fileName.substring(fileName.lastIndexOf("."))));
objectMetadata.setContentDisposition("attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 上传文件
PutObjectResult putResult = ossClient.putObject(bucketName, fileName, instream, objectMetadata);
ret = putResult.getETag();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (instream != null) {
instream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return "http://" + bucketName + "." + endpoint + "/" + fileName;
}
/**
* Description: 判断OSS服务文件上传时文件的contentType
*
* @param filenameExtension 文件后缀
* @return String
*/
public static String getcontentType(String filenameExtension) {
if (filenameExtension.equalsIgnoreCase("bmp")) {
return "image/bmp";
}
if (filenameExtension.equalsIgnoreCase("gif")) {
return "image/gif";
}
if (filenameExtension.equalsIgnoreCase("jpeg") || filenameExtension.equalsIgnoreCase("jpg")
|| filenameExtension.equalsIgnoreCase("png")) {
return "image/jpeg";
}
if (filenameExtension.equalsIgnoreCase("html")) {
return "text/html";
}
if (filenameExtension.equalsIgnoreCase("txt")) {
return "text/plain";
}
if (filenameExtension.equalsIgnoreCase("vsd")) {
return "application/vnd.visio";
}
if (filenameExtension.equalsIgnoreCase("pptx") || filenameExtension.equalsIgnoreCase("ppt")) {
return "application/vnd.ms-powerpoint";
}
if (filenameExtension.equalsIgnoreCase("docx") || filenameExtension.equalsIgnoreCase("doc")) {
return "application/msword";
}
if (filenameExtension.equalsIgnoreCase("xml")) {
return "text/xml";
}
return "application/octet-stream";
}
}