Java 将html模板转换为pdf有以下几种方式:
在这里插入图片描述

此处使用Freemarker实现的html解析,采用itext7实现的html转换为pdf,并且添加水印,最后采用阿里云OSS存储。

<dependencies>
    <!--SpringWeb-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--mybatis-->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.3.0</version>
    </dependency>
    <!--MySQL驱动-->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!--Lombok-->
    <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>
    <!--数据生成PDF所需Jar包-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <!--Freemarker html模板解析-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-freemarker</artifactId>
    </dependency>
    <!-- html转pdf -->
    <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>
    <!-- 阿里云OSS-->
    <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 {
            // 原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">
    <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) {
        // 云账号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";
  <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实现文件上传的步骤。