Java 将html模板转换为pdf有以下几种方式:
此处使用Freemarker实现的html解析,采用itext7实现的html转换为pdf,并且添加水印,最后采用阿里云OSS存储。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>4.2.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.5.0</version>
</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 {
PdfReader reader = new PdfReader(pdfFileBytes);
PdfStamper stamper = new PdfStamper(reader, outputStream);
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());
byte[] resources = PdfUtil.html2Pdf(templateContent);
byte[] resultResources = PdfUtil.addWaterMark("丁宝可", resources);
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">
<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>
<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}。
</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) {
String accessKeyId = key;
String accessKeySecret = secret;
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
String ret = "";
try {
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";
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
2. 配置OSS连接信息
在application.properties文件中配置OSS连接信息:
```properties
oss.endpoint=your-endpoint
oss.accessKeyId=your-access-key-id
oss.accessKeySecret=your-access-key-secret
oss.bucketName=your-bucket-name
3. 创建OSS客户端
在配置类中创建OSS客户端:
```java
@Configuration
public class OSSConfiguration {
@Value("${oss.endpoint}")
private String endpoint;
@Value("${oss.accessKeyId}")
private String accessKeyId;
@Value("${oss.accessKeySecret}")
private String accessKeySecret;
@Bean
public OSSClient ossClient() {
return new OSSClient(endpoint, accessKeyId, accessKeySecret);
4. 实现上传接口
```java
@RestController
public class UploadController {
@Autowired
private OSSClient ossClient;
@Value("${oss.bucketName}")
private String bucketName;
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
String fileName = file.getOriginalFilename();
try {
ossClient.putObject(bucketName, fileName, new ByteArrayInputStream(file.getBytes()));
return "success";
} catch (IOException e) {
e.printStackTrace();
} finally {
ossClient.shutdown();
return "fail";
以上就是通过阿里云OSS实现文件上传的步骤。