需求:根据某word文档,开发生成word的功能,文档内容不不固定,取自数据库,格式基本固定,提供两个接口,一个生成.docx文件,另一个生成.wps文件。
思路:springboot框架,根据原文件制作模板,使用pot-tl做数据渲染,并导出。
1、源文件、模板文件,如下


2、代码实现
pom.xml
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.29</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>2.0.4</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>
TemplateDocumentService
public class TemplateDocumentService {
public XWPFTemplate generateDocument(ReportData data) throws IOException {
// 1. 加载模板
Resource templateResource = new ClassPathResource("templates/tqnmgREPORT.docx");
XWPFTemplate template = null;
try (InputStream inputStream = templateResource.getInputStream()) {
template = XWPFTemplate.compile(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
// 2. 构建数据模型
Map<String, Object> dataModel = BeanUtil.beanToMap(data);
// 3. 渲染文档(保留模板样式)
return template.render(dataModel);
}
// 文件下载接口(支持.docx和.wps)
public ResponseEntity<Resource> exportDocument(String format, ReportData data) throws IOException {
XWPFTemplate template = generateDocument(data);
ByteArrayOutputStream out = new ByteArrayOutputStream();
template.write(out);
template.close();
String filename = "拖欠农民工报告." + format;
MediaType contentType = format.equals("docx") ?
MediaType.APPLICATION_OCTET_STREAM :
MediaType.parseMediaType("application/wps-office.wps");
return ResponseEntity.ok()
.contentType(contentType)
.header("Content-Disposition", "attachment; filename=" + filename)
.body(new ByteArrayResource(out.toByteArray()));
}
}
DocumentController
@RestController
@RequestMapping("/document")
public class DocumentController {
private final String JSONSTR = "{\n" +
" \"title\": \"关于分析...报告\",\n" +
" \"regulations\": \"按照行了汇...总分析。\",\n" +
" \"wageArrears\": {\n" +
" \"title\": \"本次成整...改。\",\n" +
" \"info\": \"其中,聚铁...四局。\",\n" +
" \"arrearsDetailList\": [\n" +
" {\n" +
" \"title\": \"(一)按建设单位及项目划分\",\n" +
" \"info\": \"\",\n" +
" \"detailList\": [\n" +
" \"1.....00万元;\"," +
" \"2. .....万元;\"," +
" \"3. .....94万元;\"," +
" \"4. .....万元;\"," +
" \"5. .....元;\"," +
" \"6. .....万元;\"," +
" \"7. .....万元;\"," +
" \"8......元。\"," +
" \"9. .....万元。\"" +
" ]\n" +
" },\n" +
" {\n" +
" \"title\": \"(二)......公司划分\",\n" +
" \"info\": \"\",\n" +
" \"detailList\": [\n" +
" \".......元。\"" +
" ]\n" +
" },\n" +
" {\n" +
" \"title\": \"(三)按.....分\",\n" +
" \"info\": \"\",\n" +
" \"detailList\": [\n" +
" \"中国.....元。\"," +
" \"中国.....万元。\"" +
" ]\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"comparisonContent\": {\n" +
" \"title\": \"\",\n" +
" \"info\": \"\",\n" +
" \"detailList\": [\n" +
" \"一是.....4%。\"," +
" \"二是.....薪事件。\"," +
" \"三是.....现象。\"" +
" ]\n" +
" },\n" +
" \"causeAnalysisContent\": {\n" +
" \"title\": \"\",\n" +
" \"info\": \"主要是....原因。\",\n" +
" \"detailList\": [\n" +
" \"一是....万元。\"," +
" \"二是.....万元。\"," +
" \"三是.....万元。\"," +
" \"四是.....元。\"," +
" \"五是.....元。\"," +
" \"六是.....元。\"" +
" ]\n" +
" },\n" +
" \"measuresContent\": {\n" +
" \"title\": \"\",\n" +
" \"info\": \"\",\n" +
" \"detailList\": [\n" +
" \"1......情。\"," +
" \"2.....中心。 \"," +
" \"3.中心....关注。\"," +
" \"4.对于....约谈。\"" +
" ]\n" +
" },\n" +
" \"department\": \"工....中心\",\n" +
" \"reportDate\": \"2029年2月27日\"\n" +
"}";
@GetMapping("test")
public String test() {
return "test-ok";
}
@Autowired
private TemplateDocumentService documentService;
@PostMapping("/download-docx")
public ResponseEntity<Resource> downloadDocx() throws IOException {
//JSONSTR 数据,该数据要从库中查询
ReportData bean = JSONUtil.toBean(JSONSTR, ReportData.class);
return buildResponse(bean, "docx");
}
@PostMapping("/download-wps")
public ResponseEntity<Resource> downloadWps() throws IOException {
ReportData bean = JSONUtil.toBean(JSONSTR, ReportData.class);
return buildResponse(bean, "wps");
}
private ResponseEntity<Resource> buildResponse(ReportData data, String format) throws IOException {
// 生成文档
XWPFTemplate template = documentService.generateDocument(data);
ByteArrayOutputStream out = new ByteArrayOutputStream();
template.write(out);
template.close();
// 设置中文文件名(核心修改点)
String rawFileName = "拖欠农民工报告." + format;
String encodedFileName = URLEncoder.encode(rawFileName, "UTF-8")
.replaceAll("\\+", "%20"); // 替换空格编码[1](@ref)[5](@ref)
// 设置响应头(兼容多浏览器)
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition",
"attachment; filename*=UTF-8''" + encodedFileName); // RFC 5987标准[6](@ref)[8](@ref)
// 根据格式设置Content-Type
MediaType contentType = format.equals("docx") ?
MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.wordprocessingml.document") :
MediaType.parseMediaType("application/wps-office.wps");
return ResponseEntity.ok()
.headers(headers)
.contentType(contentType)
.body(new ByteArrayResource(out.toByteArray()));
}
}
entity
@Data
public class DetailInfo {
private String title;
private String info;
private List<String> detailList;
}
@Data
public class ReportData {
private String title; // 动态标题
private String regulations; // 法规描述段落
private WageArrears wageArrears; //欠薪情况
private DetailInfo comparisonContent; // 同期对比情况内容
private DetailInfo causeAnalysisContent; // 欠薪...内容
private DetailInfo measuresContent; // 建议...内容
private String department; // 工...心(动态)
private String reportDate; // 报告日期(动态)
}
@Data
public class WageArrears {
private String title;
private String info;
private List<DetailInfo> arrearsDetailList;
}
代码可直接运行,导出文件内容格式与原文件一致。唯一缺陷,内容部分有些字是加粗的,没有规律,解决此问题只能在poi-tl渲染数据之前,手动设置处理个别字加粗
有问题请参照Poi-tl官方文档
[https://deepoove.com/poi-tl/#_why_poi_tl](https://deepoove.com/poi-tl/#_why_poi_tl)
浙公网安备 33010602011771号