【SpringBoot】实现HTML模板解析转换成PDF并上传OSS

Java 将html模板转换为pdf有以下几种方式:
【SpringBoot】实现HTML模板解析转换成PDF并上传OSS_第1张图片

此处使用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";
    }
}

你可能感兴趣的:(SpringBoot,spring,boot,html,pdf)