java从word模板生成.doc和.wps文件
当遇到要生成一个word文档(证明文件等)的需求时,就可以考虑使用word模板生成.doc和.wps文件一、需求
1、生成如下这样的订单数据.doc文件,红框部分是变化的,其余部分是固定的
2、生成如下这样的书籍列表,书的个数不固定是动态的。
二、使用Docx4j实现
1、引入依赖
<dependency>
<groupId>org.docx4j</groupId>
docx4j-core</artifactId>
<version>8.3.0</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
docx4j-JAXB-ReferenceImpl</artifactId>
<version>8.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.docx4j</groupId>
docx4j-JAXB-Internal</artifactId>
<version>8.3.0</version>
</dependency>注: 前两个完成第一个需求就够了,后两个使用模板完成后一个需求
2、制作模板
变量的地方用${}
模板contract_template.docx内容如下:
模板book_template.docx内容如下:
使用foreach进行循环
3、代码
Docx4jService类package com.example.demo.demos;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.Document;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import javax.xml.bind.JAXBElement;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.util.Map;
@Service
public class Docx4jService {
/**
* 根据模板生成Word文档(修复没有write方法的问题)
*/
public byte[] generateWord(String templatePath, Map<String, String> data) throws Exception {
// 1. 加载模板
ClassPathResource resource = new ClassPathResource(templatePath);
WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(resource.getInputStream());
MainDocumentPart mainDocumentPart = wordMLPackage.getMainDocumentPart();
// 2. 获取文档XML内容
Document document = mainDocumentPart.getJaxbElement();
List<Object> content = document.getBody().getContent();
// 3. 手动替换变量
replaceVariables(content, data);
// 4. 写回并生成字节数组(使用save方法替代write方法)
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
// 关键修复:用save方法替代write方法
wordMLPackage.save(out);
return out.toByteArray();
}
}
/**
* 递归替换文档中的变量(修复Text节点无法识别问题)
*/
@SuppressWarnings("unchecked")
private void replaceVariables(List<Object> content, Map<String, String> data) {
for (int i = 0; i < content.size(); i++) {
Object obj = content.get(i);
// 处理JAXB包装的元素(解开外层包装)
if (obj instanceof JAXBElement) {
obj = ((JAXBElement<?>) obj).getValue();
}
// 关键修复:处理运行元素(R),Text节点一定在R里面
if (obj instanceof org.docx4j.wml.R) {
org.docx4j.wml.R run = (org.docx4j.wml.R) obj;
// 遍历R元素中的内容,寻找Text节点
replaceVariables(run.getContent(), data);
}
// 现在可以找到Text节点了
else if (obj instanceof org.docx4j.wml.Text) {
org.docx4j.wml.Text text = (org.docx4j.wml.Text) obj;
String value = text.getValue();
// 遍历所有变量进行替换
for (Map.Entry<String, String> entry : data.entrySet()) {
String key = "${" + entry.getKey() + "}";
if (value.contains(key)) {
value = value.replace(key, entry.getValue());
text.setValue(value);
}
}
}
// 处理段落(P):继续遍历段落中的内容(会包含R元素)
else if (obj instanceof org.docx4j.wml.P) {
org.docx4j.wml.P p = (org.docx4j.wml.P) obj;
replaceVariables(p.getContent(), data);
}
// 处理表格(Tbl):继续遍历表格中的内容
else if (obj instanceof org.docx4j.wml.Tbl) {
org.docx4j.wml.Tbl tbl = (org.docx4j.wml.Tbl) obj;
replaceVariables(tbl.getContent(), data);
}
}
}
}
Book类<br>package com.example.demo.demos;
public class Book {
private String name;
private String author;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Book(String name, String author) {
this.name = name;
this.author = author;
}
} WordController类
package com.example.demo.demos;import org.docx4j.jaxb.Context;import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.core.io.ClassPathResource;import org.springframework.http.HttpHeaders;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;import javax.xml.bind.JAXBException;import javax.xml.bind.Unmarshaller;import javax.xml.stream.XMLInputFactory;import javax.xml.stream.XMLStreamException;import javax.xml.stream.XMLStreamReader;import javax.xml.transform.stream.StreamSource;import java.io.*;import java.net.URLEncoder;import java.util.*;import org.apache.velocity.VelocityContext;import org.apache.velocity.app.Velocity;import org.docx4j.Docx4J;import org.docx4j.openpackaging.packages.WordprocessingMLPackage;import org.docx4j.wml.Document;@RestController@RequestMapping("/word")public class WordController { @Autowired private Docx4jService docx4jService; /** * 生成并下载 Word 文档 */ @GetMapping("/generate") public ResponseEntity generateWord() throws Exception { // 1. 准备模板变量数据 Map data = new HashMap(); data.put("username", "张三"); data.put("orderNo", "ORD20250911001"); data.put("amount", "1000.00"); data.put("date", "2025-09-11"); // 2. 调用服务生成 Word 字节数组 byte[] wordBytes = docx4jService.generateWord("templates/contract_template.docx", data); // 3. 配置响应头,实现文件下载 String fileName = URLEncoder.encode("订单详情.docx", "UTF-8"); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment; filename=" + fileName); headers.add("Content-Type", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"); return new ResponseEntity(wordBytes, headers, HttpStatus.OK); } static { try { Properties props = new Properties(); props.setProperty("resource.loader", "class"); props.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); Velocity.init(props); } catch (Exception e) { throw new RuntimeException("Velocity引擎初始化失败", e); } } @GetMapping("/export") public void exportBooks(HttpServletResponse response) { try (InputStream templateStream = new ClassPathResource("templates/book_template.docx").getInputStream()) { WordprocessingMLPackage template = WordprocessingMLPackage.load(templateStream); MainDocumentPart mainPart = template.getMainDocumentPart(); //生成books内容 List books = new ArrayList(); for (int i = 1; i
页:
[1]