Java根据Freemarker模板生成Word文件

  1. 准备模板

模板 + 数据 = 模型

1、将准备好的Word模板文件另存为.xml文件(PS:建议使用WPS来创建Word文件,不建议用Office)

Java根据Freemarker模板生成Word文件

Java根据Freemarker模板生成Word文件

2、将.xml文件重命名为.ftl文件

Java根据Freemarker模板生成Word文件

3、用文本编辑器打开.ftl文件,将内容复制出来,格式化一下,再覆盖原来的内容

(PS:格式化一下是为了方便查找并设置变量/占位符,当然设置好模板参数变量以后可以再压缩后再写会.ftl文件)

另外,强烈不建议在word文件中去编辑设置模板变量,因为.docx文件在另存为.xml文件后,原先好好的一个变量可能就被拆开了,建议另存为之后再用文本编辑器打开去编辑。

Java根据Freemarker模板生成Word文件

4、设置模板参数(变量/占位符)

Java根据Freemarker模板生成Word文件
  1. 代码实现

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelversion>4.0.0</modelversion>
    <parent>
        <groupid>org.springframework.boot</groupid>
        <artifactid>spring-boot-starter-parent</artifactid>
        <version>2.7.3</version>
        <relativepath> <!-- lookup parent from repository -->
    </relativepath></parent>
    <groupid>com.example</groupid>
    <artifactid>demo920</artifactid>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo920</name>
    <description>demo920</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-freemarker</artifactid>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <dependency>
            <groupid>cn.hutool</groupid>
            <artifactid>hutool-core</artifactid>
            <version>5.8.7</version>
        </dependency>
        <dependency>
            <groupid>com.itextpdf</groupid>
            <artifactid>itextpdf</artifactid>
            <version>5.5.13.3</version>
        </dependency>
        <!--<dependency>
            <groupId>com.aspose</groupId>
            <artifactId>aspose-words</artifactId>
            <version>22.9</version>
            <classifier>jdk17</classifier>
        </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>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupid>org.springframework.boot</groupid>
                <artifactid>spring-boot-maven-plugin</artifactid>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupid>org.projectlombok</groupid>
                            <artifactid>lombok</artifactid>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

写个类测试一下

package com.example.demo920;

import com.example.demo920.domain.LoanReceipt;

import freemarker.template.Configuration;
import freemarker.template.Template;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

@SpringBootTest
class Demo920ApplicationTests {

    private DateTimeFormatter DTF = DateTimeFormatter.ofPattern("yyyyMMddHHmmss", Locale.CHINA);

    @Test
    void contextLoads() {
    }

    @Test
    void testGenerateWord() throws Exception {
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_31);
        configuration.setDefaultEncoding("UTF-8");
        configuration.setClassForTemplateLoading(this.getClass(), "/templates");

        Template template = configuration.getTemplate("&#x501F;&#x6761;.ftl");

        Path path = Paths.get("tmp","contract");
        File fileDir = path.toFile();
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }

        String filename = "&#x501F;&#x6761;" + "_" + LocalDateTime.now().format(DTF) + ".docx";
        filename = path.toFile() + File.separator + filename;

        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename)));

//        template.process(getDataMap(), writer);
        template.process(getData(), writer);

        writer.flush();
        writer.close();
    }

    Map<string, object> getDataMap() {
        Map<string, object> dataMap = new HashMap<>();
        dataMap.put("borrowerName", "&#x674E;&#x767D;");
        dataMap.put("borrowerIdCard", "421302199001012426");
        dataMap.put("lenderName", "&#x675C;&#x752B;");
        dataMap.put("amount", 100);
        dataMap.put("amountInWords", "&#x58F9;&#x4F70;");
        dataMap.put("startDate", "2022&#x5E74;8&#x6708;15&#x65E5;");
        dataMap.put("endDate", "2022&#x5E74;11&#x6708;11&#x65E5;");
        dataMap.put("borrowingMonths", 3);
        dataMap.put("interestRate", "1.23");
        dataMap.put("guarantorName", "&#x767D;&#x5C45;&#x6613;");
        dataMap.put("guarantorIdCard", "421302199203152412");
        return dataMap;
    }

    LoanReceipt getData() {
        LoanReceipt receipt = new LoanReceipt();
        receipt.setBorrowerName("&#x72C4;&#x4EC1;&#x6770;");
        receipt.setBorrowerIdCard("421302198710121234");
        receipt.setBorrowingMonths(6);
        receipt.setLenderName("&#x674E;&#x5143;&#x82B3;");
        receipt.setAmount(new BigDecimal("101"));
        receipt.setAmountInWords("&#x58F9;&#x4F70;&#x96F6;&#x58F9;");
        receipt.setInterestRate(new BigDecimal("0.6"));
        receipt.setStartDate("2022&#x5E74;1&#x6708;1&#x65E5;");
        receipt.setEndDate("2022&#x5E74;6&#x6708;30&#x65E5;");
        receipt.setGuarantorName("&#x6B66;&#x5219;&#x5929;");
        receipt.setGuarantorIdCard("421302199101014567");
        return receipt;
    }

}</string,></string,>

最主要的是下面两行

//  &#x52A0;&#x8F7D;&#x6A21;&#x677F;
Template template = configuration.getTemplate("&#x501F;&#x6761;.ftl");
//  &#x586B;&#x5145;&#x6570;&#x636E;
template.process(getData(), writer);

数据可以是Map也可以是一个对象

改进一下,将生成文件的操作单独写成一个工具方法

package com.example.demo920.util;

import cn.hutool.core.io.IoUtil;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.*;

public class FreemarkerUtils {

    /**
     * &#x751F;&#x6210;Word
     * @param templateDir   &#x6A21;&#x677F;&#x6240;&#x5728;&#x7684;&#x76EE;&#x5F55;
     * @param templateName  &#x6A21;&#x677F;&#x6587;&#x4EF6;&#x540D;&#x79F0;
     * @param filename      &#x751F;&#x6210;&#x7684;&#x6587;&#x4EF6;&#xFF08;&#x542B;&#x8DEF;&#x5F84;&#xFF09;
     * @param dataModel     &#x6A21;&#x677F;&#x53C2;&#x6570;&#x6570;&#x636E;
     */
    public static void generateWord(File templateDir, String templateName, String filename, Object dataModel) {
        BufferedWriter writer = null;
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_31);
        configuration.setDefaultEncoding("UTF-8");
        try {
            configuration.setDirectoryForTemplateLoading(templateDir);
            Template template = configuration.getTemplate(templateName);
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename)));
            template.process(dataModel, writer);
            writer.flush();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (TemplateException e) {
            throw new RuntimeException(e);
        } finally {
            IoUtil.close(writer);
        }
    }

}

再测试一下

package com.example.demo920;

import cn.hutool.core.io.IoUtil;
import com.example.demo920.util.FreemarkerUtils;
import com.example.demo920.util.PdfUtils;
import org.junit.jupiter.api.Test;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

public class WordTest {

    /**
     * 1&#x3001;&#x4ECE;&#x6587;&#x4EF6;&#x670D;&#x52A1;&#x5668;&#x4E0B;&#x8F7D;&#x6A21;&#x677F;&#x6587;&#x4EF6;
     * 2&#x3001;&#x6839;&#x636E;&#x4E1A;&#x52A1;&#x7C7B;&#x578B;&#x83B7;&#x53D6;&#x9700;&#x8981;&#x586B;&#x5145;&#x6A21;&#x677F;&#x7684;&#x6570;&#x636E;
     * 3&#x3001;&#x6A21;&#x677F;+&#x6570;&#x636E;  &#x518D;&#x7ECF;&#x8FC7;&#x5904;&#x7406;&#x751F;&#x6210;&#x65B0;&#x7684;&#x6587;&#x4EF6;
     * 4&#x3001;&#x5C06;&#x751F;&#x6210;&#x540E;&#x7684;&#x6587;&#x4EF6;&#x4E0A;&#x4F20;&#x5230;&#x6587;&#x4EF6;&#x670D;&#x52A1;&#x5668;&#xFF0C;&#x5E76;&#x8FD4;&#x56DE;&#x4E00;&#x4E2A;&#x6587;&#x4EF6;ID
     * 5&#x3001;&#x4E1A;&#x52A1;&#x53EF;&#x4EE5;&#x4FDD;&#x5B58;&#x8FD9;&#x4E2A;&#x6587;&#x4EF6;ID&#x6216;&#x8005;&#x6587;&#x4EF6;&#x7684;&#x8DEF;&#x5F84;
     */
    @Test
    void testGenerateWordV1() throws Exception {
        Path tempPath = Paths.get("tmp", "contract2");
        File path = tempPath.toFile();
        if (!path.exists()) {
            path.mkdirs();
        }
        File tempFile = Files.createTempFile(tempPath, "qiantiao", ".docx").toFile();
        System.out.println(tempFile.getParent());
        System.out.println(tempFile.getName());
        FileOutputStream fos = new FileOutputStream(tempFile);

        File templateFile = ResourceUtils.getFile("classpath:templates/&#x501F;&#x6761;.ftl");
        FileInputStream fis = new FileInputStream(templateFile);

        IoUtil.copy(fis, fos);

        String filename = "&#x501F;&#x6761;" + "_" + System.currentTimeMillis() + ".docx";
        filename = "tmp/contract" + File.separator + filename;

        FreemarkerUtils.generateWord(new File(tempFile.getParent()), tempFile.getName(), filename, getDataMap());
    }

    /**
     * &#x83B7;&#x53D6;&#x6570;&#x636E;
     */
    Map<string, object> getDataMap() {
        Map<string, object> dataMap = new HashMap<>();
        dataMap.put("borrowerName", "&#x674E;&#x767D;2");
        dataMap.put("borrowerIdCard", "421302199001012426");
        dataMap.put("lenderName", "&#x675C;&#x752B;");
        dataMap.put("amount", 100);
        dataMap.put("amountInWords", "&#x58F9;&#x4F70;");
        dataMap.put("startDate", "2022&#x5E74;8&#x6708;15&#x65E5;");
        dataMap.put("endDate", "2022&#x5E74;11&#x6708;11&#x65E5;");
        dataMap.put("borrowingMonths", 3);
        dataMap.put("interestRate", "1.23");
        dataMap.put("guarantorName", "&#x767D;&#x5C45;&#x6613;");
        dataMap.put("guarantorIdCard", "421302199203152412");
        return dataMap;
    }

    @Test
    void testGenerateWord2() throws Exception {
        File templateDir = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "templates");
        String templateName = "&#x501F;&#x6761;.ftl";
        String destFilename = "&#x501F;&#x6761;" + System.currentTimeMillis() + ".docx";
        Map<string, object> data = getDataMap();
        FreemarkerUtils.generateWord(templateDir, templateName, destFilename, data);
    }

}</string,></string,></string,>

Java根据Freemarker模板生成Word文件
  1. PDF文件加水印

有时候,生成或者从服务器下载的文件是需要加水印的,比如标识这个文件是谁下载的之类的

pdf加水印还是比较方便的,用itext组件可以轻松实现

另外,如果最终需要pdf文件,建议直接生成pdf文件,跳过word转pdf的步骤

package com.example.demo920.util;

import cn.hutool.core.io.IoUtil;
import com.aspose.words.Document;
import com.aspose.words.SaveFormat;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Image;
import com.itextpdf.text.pdf.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;

/**
 * @author chengjiansheng
 * @date 2022/09/21
 */
public class PdfUtils {

    /**
     * Word&#x8F6C;PDF
     * https://www.aspose.com/
     * &#x6CE8;&#x610F;&#xFF1A;Aspose.Words &#x8FD9;&#x4E2A;&#x7EC4;&#x4EF6;&#x662F;&#x6536;&#x8D39;&#x7684;&#xFF0C;&#x5982;&#x679C;&#x8D2D;&#x4E70;&#x7684;&#x8BDD;&#x751F;&#x6210;&#x7684;PDF&#x4F1A;&#x6709;&#x6C34;&#x5370;&#x3002;
     * &#x53EF;&#x4EE5;&#x53BB;&#x627E;&#x76F8;&#x5E94;&#x7684;&#x7834;&#x89E3;&#x7248;&#x672C;&#xFF0C;&#x4F46;&#x662F;&#x6211;&#x611F;&#x89C9;&#x5B8C;&#x5168;&#x53EF;&#x4EE5;&#x8DF3;&#x8FC7;Word&#x76F4;&#x63A5;&#x751F;&#x6210;PDF&#x3002;
     * &#x6BD4;&#x5982;&#xFF0C;&#x53EF;&#x4EE5;&#x901A;&#x8FC7;Freemarker&#x76F4;&#x63A5;&#x751F;&#x6210;PDF&#xFF0C;&#x6216;&#x8005;&#x5229;&#x7528;iText&#x901A;&#x8FC7;&#x6A21;&#x677F;&#x751F;&#x6210;PDF
     * @param src
     * @param dest
     */
    public static void wordToPdf(String src, String dest) {
        File file = new File(src);
        if (!file.exists()) {
            throw new RuntimeException("&#x6587;&#x4EF6;&#x4E0D;&#x5B58;&#x5728;");
        }
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(file);
            Document wpd = new Document(fis);
            wpd.save(dest, SaveFormat.PDF);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            IoUtil.close(fis);
        }
    }

    /**
     * &#x52A0;&#x6C34;&#x5370;
     * @param src   &#x6E90;&#x6587;&#x4EF6;
     * @param dest  &#x76EE;&#x6807;&#x6587;&#x4EF6;
     * @param text  &#x6587;&#x5B57;
     * @param imagePath &#x56FE;&#x7247;&#x5730;&#x5740;
     */
    public static void addWatermark(String src, String dest, String text, String imagePath) {
        try {
            //  &#x5F85;&#x52A0;&#x6C34;&#x5370;&#x7684;&#x6587;&#x4EF6;
            PdfReader reader = new PdfReader(src);
            //  &#x52A0;&#x5B8C;&#x6C34;&#x5370;&#x7684;&#x6587;&#x4EF6;
            PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
            //  &#x5B57;&#x4F53;
            BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            //  &#x900F;&#x660E;&#x5EA6;
            PdfGState gs = new PdfGState();
            gs.setFillOpacity(0.4f);
            //  PDF&#x6587;&#x4EF6;&#x603B;&#x9875;&#x6570;
            int total = reader.getNumberOfPages() + 1;
            //  &#x5FAA;&#x73AF;&#x5BF9;&#x6BCF;&#x4E00;&#x9875;&#x90FD;&#x52A0;&#x6C34;&#x5370;
            PdfContentByte content;
            for (int i = 1; i < total; i++) {
                //  &#x6C34;&#x5370;&#x5728;&#x6587;&#x672C;&#x4E4B;&#x4E0A;
                content = stamper.getOverContent(i);
                content.setGState(gs);

                if (null != imagePath) {
                    Image image = Image.getInstance(imagePath);
//                    image.setAbsolutePosition(150, 150);
//                    image.scaleToFit(300,300);
//                    content.addImage(image);

                    for (int x = 0; x < 700; x = x + 300) {
                        for (int y = 0; y < 900; y = y + 200) {
                            image.setAbsolutePosition(x+50, y+50);
                            image.scaleToFit(100,100);
                            content.addImage(image);
                        }
                    }
                }
                if (null != text) {
                    content.beginText();
                    content.setColorFill(BaseColor.RED);
                    content.setFontAndSize(baseFont, 20);
//                    content.showTextAligned(Element.ALIGN_CENTER, text, 50, 50, 45);

                    for (int x = 0; x < 700; x = x + 300) {
                        for (int y = 0; y < 900; y = y + 200) {
                            //&#x6C34;&#x5370;&#x5185;&#x5BB9;&#x548C;&#x6C34;&#x5370;&#x4F4D;&#x7F6E;
                            content.showTextAligned(Element.ALIGN_CENTER, "&#x54C8;&#x54C8;&#x54C8;&#x54C8;&#x54C8;", x - 20, y + 10, 30);
                            content.showTextAligned(Element.ALIGN_CENTER, LocalDateTime.now().toString(), x, y, 30);
                        }
                    }

                    content.endText();
                }
            }
            stamper.close();
            reader.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        }
    }
}

跑一下

@Test
void testWatermark() {
    String src2 = "D:\\&#x501F;&#x6761;_2.pdf";
    String dest2 = "D:\\&#x501F;&#x6761;_3.pdf";
    String imagePath = "D:\\1.jpg";
    PdfUtils.addWatermark(src2, dest2, "&#x54C8;&#x54C8;&#x54C8;&#x54C8;&#x54C8;", imagePath);
}

加完水印后效果如图

Java根据Freemarker模板生成Word文件

最后,示例项目结构如图

Java根据Freemarker模板生成Word文件

参考:

https://blog.csdn.net/weixin_45103378/article/details/118395284

Original: https://www.cnblogs.com/cjsblog/p/16715294.html
Author: 废物大师兄
Title: Java根据Freemarker模板生成Word文件

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/611553/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球