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文件

2.  代码实现

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 -->     </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("借条.ftl");          Path path = Paths.get("tmp","contract");         File fileDir = path.toFile();         if (!fileDir.exists()) {             fileDir.mkdirs();         }          String filename = "借条" + "_" + 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", "李白");         dataMap.put("borrowerIdCard", "421302199001012426");         dataMap.put("lenderName", "杜甫");         dataMap.put("amount", 100);         dataMap.put("amountInWords", "壹佰");         dataMap.put("startDate", "2022年8月15日");         dataMap.put("endDate", "2022年11月11日");         dataMap.put("borrowingMonths", 3);         dataMap.put("interestRate", "1.23");         dataMap.put("guarantorName", "白居易");         dataMap.put("guarantorIdCard", "421302199203152412");         return dataMap;     }      LoanReceipt getData() {         LoanReceipt receipt = new LoanReceipt();         receipt.setBorrowerName("狄仁杰");         receipt.setBorrowerIdCard("421302198710121234");         receipt.setBorrowingMonths(6);         receipt.setLenderName("李元芳");         receipt.setAmount(new BigDecimal("101"));         receipt.setAmountInWords("壹佰零壹");         receipt.setInterestRate(new BigDecimal("0.6"));         receipt.setStartDate("2022年1月1日");         receipt.setEndDate("2022年6月30日");         receipt.setGuarantorName("武则天");         receipt.setGuarantorIdCard("421302199101014567");         return receipt;     }  }

最主要的是下面两行

//	加载模板 Template template = configuration.getTemplate("借条.ftl"); //	填充数据 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 {      /**      * 生成Word      * @param templateDir   模板所在的目录      * @param templateName  模板文件名称      * @param filename      生成的文件(含路径)      * @param dataModel     模板参数数据      */     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、从文件服务器下载模板文件      * 2、根据业务类型获取需要填充模板的数据      * 3、模板+数据  再经过处理生成新的文件      * 4、将生成后的文件上传到文件服务器,并返回一个文件ID      * 5、业务可以保存这个文件ID或者文件的路径      */     @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/借条.ftl");         FileInputStream fis = new FileInputStream(templateFile);          IoUtil.copy(fis, fos);          String filename = "借条" + "_" + System.currentTimeMillis() + ".docx";         filename = "tmp/contract" + File.separator + filename;          FreemarkerUtils.generateWord(new File(tempFile.getParent()), tempFile.getName(), filename, getDataMap());     }   	/**      * 获取数据      */     Map<String, Object> getDataMap() {         Map<String, Object> dataMap = new HashMap<>();         dataMap.put("borrowerName", "李白2");         dataMap.put("borrowerIdCard", "421302199001012426");         dataMap.put("lenderName", "杜甫");         dataMap.put("amount", 100);         dataMap.put("amountInWords", "壹佰");         dataMap.put("startDate", "2022年8月15日");         dataMap.put("endDate", "2022年11月11日");         dataMap.put("borrowingMonths", 3);         dataMap.put("interestRate", "1.23");         dataMap.put("guarantorName", "白居易");         dataMap.put("guarantorIdCard", "421302199203152412");         return dataMap;     }      @Test     void testGenerateWord2() throws Exception {         File templateDir = ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX + "templates");         String templateName = "借条.ftl";         String destFilename = "借条" + System.currentTimeMillis() + ".docx";         Map<String, Object> data = getDataMap();         FreemarkerUtils.generateWord(templateDir, templateName, destFilename, data);     }  }

Java根据Freemarker模板生成Word文件

3. 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转PDF      * https://www.aspose.com/      * 注意:Aspose.Words 这个组件是收费的,如果购买的话生成的PDF会有水印。      * 可以去找相应的破解版本,但是我感觉完全可以跳过Word直接生成PDF。      * 比如,可以通过Freemarker直接生成PDF,或者利用iText通过模板生成PDF      * @param src      * @param dest      */     public static void wordToPdf(String src, String dest) {         File file = new File(src);         if (!file.exists()) {             throw new RuntimeException("文件不存在");         }         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);         }     }      /**      * 加水印      * @param src   源文件      * @param dest  目标文件      * @param text  文字      * @param imagePath 图片地址      */     public static void addWatermark(String src, String dest, String text, String imagePath) {         try {             //  待加水印的文件             PdfReader reader = new PdfReader(src);             //  加完水印的文件             PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));             //  字体             BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);             //  透明度             PdfGState gs = new PdfGState();             gs.setFillOpacity(0.4f);             //  PDF文件总页数             int total = reader.getNumberOfPages() + 1;             //  循环对每一页都加水印             PdfContentByte content;             for (int i = 1; i < total; i++) {                 //  水印在文本之上                 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) {                             //水印内容和水印位置                             content.showTextAligned(Element.ALIGN_CENTER, "哈哈哈哈哈", 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:\借条_2.pdf";     String dest2 = "D:\借条_3.pdf";     String imagePath = "D:\1.jpg";     PdfUtils.addWatermark(src2, dest2, "哈哈哈哈哈", imagePath); }

加完水印后效果如图

Java根据Freemarker模板生成Word文件

最后,示例项目结构如图

Java根据Freemarker模板生成Word文件

 

参考:

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

发表评论

评论已关闭。

相关文章