一、背景介绍:
在我们日常工作中,难免遇到需要导出docx文件的时候,也就是导出word文件,今天就简单介绍一下通过POI的XWPFDocument来导出word文件。
二、代码实现:
2.1、word文本
我们给导出的word文件写一段文字。
package com.relation.web.controller.export;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.Objects;
/**
* @author huhy
* @version 1.0
* @Description:
* @ClassName Date:2025/6/28 23:17
*/
@Slf4j
@RestController
@RequestMapping("/testExportController")
public class TestExportController {
@PostMapping("/exportWordFile")
public void exportWordFile(HttpServletResponse response){
//创建XWPFDocument
XWPFDocument doc = new XWPFDocument();
//创建段落
XWPFParagraph p = doc.createParagraph();
XWPFRun run = p.createRun();
run.setText("这是一个测试文本");
run.setFontSize(14);
ServletOutputStream outputStream = null;
try{
outputStream = response.getOutputStream();
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode("测试导出.docx", "UTF-8"));
doc.write(outputStream);
}catch (Exception e){
log.error(e.getMessage(),e);
throw new RuntimeException("导出异常");
}finally {
if(Objects.nonNull(outputStream)){
try {
outputStream.close();
doc.close();
}catch (Exception e){
}
}
}
}
}
2.2、word表格
2.1中给word中写了一段文字,这次我们给word文件写一个表格。
package com.relation.web.controller.export;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STTblWidth;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.math.BigInteger;
import java.net.URLEncoder;
import java.util.Objects;
/**
* @author huhy
* @version 1.0
* @Description:
* @ClassName Date:2025/6/28 23:17
*/
@Slf4j
@RestController
@RequestMapping("/testExportController")
public class TestExportController {
@PostMapping("/exportWordFile")
public void exportWordFile(HttpServletResponse response){
//创建XWPFDocument
XWPFDocument doc = new XWPFDocument();
//创建段落
XWPFParagraph p = doc.createParagraph();
XWPFRun run = p.createRun();
run.setText("这是一个测试文本");
run.setFontSize(14);
//初始化一个两行、4列的表格
XWPFTable table = doc.createTable(2, 4);
CTTblWidth width = table.getCTTbl().addNewTblPr().addNewTblW();
width.setType(STTblWidth.PCT);
width.setW(BigInteger.valueOf(5000)); // 5000=100%
table.getRow(0).getCell(0).setText("导出测试表格");
String[] headers = {"序号","姓名","年龄","城市"};
for(int i=0; i<headers.length; i++) {
table.getRow(1).getCell(i).setText(headers[i]);
}
// 添加数据行
String[][] data = {{"1","小永哥","18","北京"},
{"2","胡彪","45","吉林"}};
for(String[] rowData : data) {
XWPFTableRow row = table.createRow();
for(int i=0; i<rowData.length; i++) {
row.getCell(i).setText(rowData[i]);
}
}
ServletOutputStream outputStream = null;
try{
outputStream = response.getOutputStream();
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode("测试导出.docx", "UTF-8"));
doc.write(outputStream);
}catch (Exception e){
log.error(e.getMessage(),e);
throw new RuntimeException("导出异常");
}finally {
if(Objects.nonNull(outputStream)){
try {
outputStream.close();
doc.close();
}catch (Exception e){
}
}
}
}
}
2.3、基于模板导出
在2.1和2.2步骤中,我们已经实现了简单的文本和表格的输出,不过代码基本上写死了,不太灵活,其实在实际开发过程中,常用的套路是创建模板,然后用真实的数据替换模板中的占位符或者是书签来达到动态生成文件的效果,我们简单来实现一下。
2.3.1、首先创建一个模板,然后给模板中添加一下书签。
2.3.2、将创建好书签的模板存放到某个位置,这个位置可以是本地、也可以是文件服务器上,这个按实际项目要求来就好。
package com.relation.web.controller.export;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBookmark;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.List;
import java.util.Objects;
/**
* @author huhy
* @version 1.0
* @Description:
* @ClassName Date:2025/6/28 23:17
*/
@Slf4j
@RestController
@RequestMapping("/testExportController")
public class TestExportController {
@PostMapping("/exportWordFile")
public void exportWordFile(HttpServletResponse response){
Resource resource = new ClassPathResource("model/导出模板.docx");
//创建XWPFDocument
XWPFDocument doc = null;
ServletOutputStream outputStream = null;
try{
doc = new XWPFDocument(resource.getInputStream());
//获取段落
List<XWPFParagraph> paragraphs = doc.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
//从段落中获取书签
List<CTBookmark> bookmarkStartList = paragraph.getCTP().getBookmarkStartList();
for (CTBookmark ctBookmark : bookmarkStartList) {
String ctBookmarkName = ctBookmark.getName();
if("exportTitle".equals(ctBookmarkName)){
// 清除原有内容
paragraph.getRuns().forEach(run -> run.setText("", 0));
// 添加新内容
paragraph.createRun().setText("导出测试标题!!");
}
if("tableTitle".equals(ctBookmarkName)){
// 清除原有内容
paragraph.getRuns().forEach(run -> run.setText("", 0));
// 添加新内容
paragraph.createRun().setText("测试导出表格标题!!");
}
if("tableInfo".equals(ctBookmarkName)){
// 在书签位置创建表格
XWPFTable table = doc.insertNewTbl(paragraph.getCTP().newCursor());
//删除默认行
table.removeRow(0);
//插入3行
for (int i = 0; i < 3; i++) {
table.createRow();
}
List<XWPFTableRow> rows = table.getRows();
String[] headers = {"序号","姓名","年龄","城市"};
XWPFTableRow headerRow = table.getRow(0);
for(int i=0; i<headers.length; i++) {
headerRow.createCell().setText(headers[i]);
}
// 添加数据行
String[][] data = {{"1","小永哥","18","北京"},
{"2","胡彪","45","吉林"}};
for (int i = 0; i < data.length; i++) {
XWPFTableRow row = table.getRow(i+1);
String[] rowData = data[i];
for(int j=0; j<rowData.length; j++) {
row.createCell().setText(rowData[j]);
}
}
}
}
}
//检查段落中的书签
outputStream = response.getOutputStream();
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
response.setHeader("Content-Disposition", "attachment; filename="+ URLEncoder.encode("测试导出.docx", "UTF-8"));
doc.write(outputStream);
}catch (Exception e){
log.error(e.getMessage(),e);
throw new RuntimeException("导出异常");
}finally {
if(Objects.nonNull(outputStream)){
try {
outputStream.close();
doc.close();
}catch (Exception e){
}
}
}
}
}
可以看到,我们通过在模板中设置书签,在代码中定位书签替换内容就能完成导出word,虽然XWPFDocument类还有其他的功能,不过经过这次分享,大家应该对XWPFDocument有了一定的了解,其他的功能就不多做介绍了,大家自行发掘。
三、结语
导出word说实话不是什么冷门的需求,但是大多数老铁听说过但是自己做的少,比如说我吧,之前就有这个需求,但任务当时是安排给了其他同事,所以小永哥就没再多做了解,所以对这个功能一直处于一知半解的,直到有一天自己遇到了这个需求才真正花精力去学习了解了一番。
好了本次就先分享到这里,如果对老铁们有帮助,还希望可以给小永哥点个赞,谢谢大家......