这篇文档手把手教你完成导出word套打,有这个demo,其他word套打导出都通用。
1、主要依赖
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>repository.org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.22</version>
</dependency>
<dependency>
<groupId>repository.org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
2、模板excel见附件。
3、以下为导出代码逻辑:
package com.example.demo.excel;
import cn.hutool.poi.word.PicType;
import lombok.Data;
/**
* 图片对象
* @author xiajun
*/
@Data
public class WordImage {
/**
* 图片宽度
*/
private int width = 100;
/**
* 图片高度
*/
private int height = 100;
/**
* 图片地址
* resource资源相对路径
*/
private String path;
/**
* 字节流
*/
private byte[] source;
/**
* 图片类型
* 默认PNG
*
*/
private PicType imageType = PicType.PNG;
}
package com.example.demo.excel;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.word.Word07Writer;
import cn.hutool.poi.word.WordUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.web.bind.annotation.GetMapping;
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.io.IOException;
import java.util.*;
/**
* word导出
* @author xiajun
*/
@Slf4j
@RestController
@RequestMapping("mvc/word")
public class WordExport {
@GetMapping(path = "/export")
public void export(HttpServletResponse response) throws IOException {
//1、============================模拟数据===============================
Map<String, Object> params = new HashMap<>();
params.put("${name}", "张三");
params.put("${sex}", "男");
params.put("${birth}", "1999.11");
params.put("${age}", "25");
params.put("${nation}", "汉族");
params.put("${npn}", "四川成都");
params.put("${bpn}", "四川成都");
params.put("${pt}", "2017.02");
params.put("${workTime}", "2016.03");
params.put("${hn}", "健康");
params.put("${beFawts}", "打篮球");
params.put("${proTec}", "高级工程师");
params.put("${degree}", "大学本科");
params.put("${education}", "文学学士");
params.put("${school}", "北京大学文院");
params.put("${major}", "语言学");
params.put("${dutyDegree}", "研究生毕业");
params.put("${dutyEducation}", "其他");
params.put("${dutySchool}", "四川大学土木学院");
params.put("${dutyMajor}", "工程造价");
params.put("${jobName}", "党委书记,组织部长,宣传部部长");
//图片设置
WordImage wordImage = new WordImage();
wordImage.setHeight(150);
wordImage.setWidth(100);
byte[] imageBytes = FileUtil.readBytes("templates/fm.png");
wordImage.setSource(imageBytes);
params.put("@{photo}", wordImage);
//一段长文本拆分,按照分行进行展示,例如简历这种
String resume = "2017.08--2018.08 北京大学光华管理学院工商管理就读研究生\r2023.05--2023.09 简历测试职务\r2023.05--2023.09 外部人员挂职职务\r2023.09-- 图书档案资料人员";
params.put("${resume}", resume);
//2、============================获取word模板===============================
Word07Writer writer = WordUtil.getWriter(FileUtil.file("templates/mb.docx"));
XWPFDocument document = writer.getDoc();//获取模板文档
//3、============================获取模板文本的段落===============================
Iterator<XWPFParagraph> paragraphStrings = document.getParagraphsIterator();
while (paragraphStrings.hasNext()) {
XWPFParagraph paragraph = paragraphStrings.next();
log.info("文本段落文字:{}", paragraph.getText());
}
//4、============================获取模板表格的段落===============================
Iterator<XWPFTable> tableIterator = document.getTablesIterator();
while (tableIterator.hasNext()) {
XWPFTable table = tableIterator.next();
Iterator<XWPFTableRow> rows = table.getRows().listIterator();
while (rows.hasNext()) {
XWPFTableRow row = rows.next();
Iterator<XWPFTableCell> cells = row.getTableCells().listIterator();
while (cells.hasNext()) {
XWPFTableCell cell = cells.next();
List<XWPFParagraph> paragraphCells = cell.getParagraphs();
//只去表格中的一个段落
XWPFParagraph paragraph = CollectionUtil.isNotEmpty(paragraphCells) ? paragraphCells.get(0) : null;
if(paragraph != null){
String paragraphString = paragraph.getText();//段落中的文字
//占位符替换
TemplateProcessor.matchTemplate(paragraphString, params, paragraph);
}
}
}
}
//5、======================输出文件====================================
// 设置content—type
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset:utf-8");
response.setHeader("Content-Disposition", "attachment;filename=data.docx");
// 设置标题
//Content-disposition是MIME协议的扩展,MIME协议指示MIME用户代理如何显示附加的文件。
ServletOutputStream outputStream = response.getOutputStream();
//将Writer刷新到OutPut
writer.flush(outputStream, true);
outputStream.close();
}
}
package com.example.demo.excel;
import cn.hutool.poi.word.PicType;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
/**
* word模板处理
* @author xiajun
*/
@Slf4j
public class TemplateProcessor {
final static Pattern patternString = Pattern.compile("\\$\\{[^{}]+}");//匹配文字
final static Pattern patternTable = Pattern.compile("#\\{[^{}]+}");//匹配表格
final static Pattern patternImg = Pattern.compile("@\\{[^{}]+}");//匹配照片
/**
* 模板占位符匹配
*
* @param template
* @param variables
* @return
*/
public static void matchTemplate(String template, Map<String, Object> variables, XWPFParagraph paragraph) {
//文字占位符替换
Matcher matcher = patternString.matcher(template);
while (matcher.find()) {
String variable = matcher.group();//占位符
dealXWPFRun(paragraph,variables,variable);//文档中占位符去除并设置套打文字
}
//图片占位符替换
Matcher matcherImg = patternImg.matcher(template);
while (matcherImg.find()) {
String variable = matcherImg.group();//占位符
dealXWPFRun(paragraph,variables,variable);//文档中占位符去除并设置套打图片
}
}
/**
* 获取文档占位符run,并且进行替换
* @param paragraph
* @param variables
* @param variable
*/
public static void dealXWPFRun(XWPFParagraph paragraph, Map<String, Object> variables, String variable){
Object object = variables.get(variable);//需要替换的对象
if (paragraph != null) {
//获取占位符第一个
String beginTag = variable.substring(0, 1);
Iterator<XWPFRun> runs = paragraph.getRuns().listIterator();
boolean flag = false;//是否开始拼接
StringBuffer placeholder = new StringBuffer();
List<List<XWPFRun>> allRuns = new ArrayList<>();//所有占位符涉及到的run
List<XWPFRun> currentRuns = new ArrayList<>();//当前占位符涉及到的run
while (runs.hasNext()) {
XWPFRun run = runs.next();
//开始符号处理
if (run.text().contains(beginTag)) {
flag = true;
}
if (flag) {
String runString = run.text();
placeholder.append(runString);
currentRuns.add(run);
}
if (variable.equals(placeholder.toString())) {
allRuns.add(currentRuns);
currentRuns = new ArrayList<>();//重置list
flag = false;
}
}
//将占位符涉及到的每个run list的第一个元素进行赋值,其他设置为空
Iterator<List<XWPFRun>> iterator = allRuns.listIterator();
while (iterator.hasNext()) {
List<XWPFRun> runList = iterator.next();
for (int i = 0; i < runList.size(); i++) {
if (i == 0) {
//文字处理
if(object instanceof String){
setRunText(runList.get(i),(String)object);
}
//图片处理
if(object instanceof WordImage){
setRunImg(runList.get(i),(WordImage)object);
}
} else {
runList.get(i).setText("", 0);
}
}
}
}
}
/**
* 设置Run中的图片
* @param run
* @param wordImage
*/
private static void setRunImg(XWPFRun run, WordImage wordImage){
PicType picType = wordImage.getImageType();
try {
run.setText("", 0);
//往文档加入图片
String picId = run.getDocument().addPictureData(wordImage.getSource(), picType.getValue());
//将图片放在指定run中
insertImageInRun(run, picId, run.getDocument().getNextPicNameNumber(picType.getValue()),
wordImage.getWidth(), wordImage.getHeight());
} catch (InvalidFormatException e) {
e.printStackTrace();
}
}
/**
* 设置Run中的文本
*
* @param run
* @param text
*/
private static void setRunText(XWPFRun run, String text) {
if (text.contains("\r")) {
String[] split = text.split("\r");
List<String> contents = Arrays.asList(split);
run.setText(contents.get(0), 0);
XWPFParagraph paragraph =(XWPFParagraph)run.getParent();
XWPFTableCell cell = (XWPFTableCell)paragraph.getBody();
for (int i = 1; i < contents.size(); i++) {
//替换的肯定有段落获取第一个段落
XWPFParagraph newParagraph = cell.addParagraph();
newParagraph.getCTP().setPPr(paragraph.getCTP().getPPr());
XWPFRun newRun = newParagraph.createRun();
newRun.getCTR().setRPr(run.getCTR().getRPr());
String content = contents.get(i);
newRun.setText(content);
}
} else {
run.setText(text,0);
}
}
/**
* run中插入图片
*
* @param run
* @param picId
* @param id
* @param width
* @param height
*/
private static void insertImageInRun(XWPFRun run, String picId, int id, int width, int height) {
final int EMU = 9525;
width *= EMU;
height *= EMU;
CTInline inline = run.getCTR().addNewDrawing().addNewInline();
String picXml = "" +
"<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">" +
" <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
" <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">" +
" <pic:nvPicPr>" +
" <pic:cNvPr id=\"" + id + "\" name=\"Generated\"/>" +
" <pic:cNvPicPr/>" +
" </pic:nvPicPr>" +
" <pic:blipFill>" +
" <a:blip r:embed=\"" + picId + "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>" +
" <a:stretch>" +
" <a:fillRect/>" +
" </a:stretch>" +
" </pic:blipFill>" +
" <pic:spPr>" +
" <a:xfrm>" +
" <a:off x=\"0\" y=\"0\"/>" +
" <a:ext cx=\"" + width + "\" cy=\"" + height + "\"/>" +
" </a:xfrm>" +
" <a:prstGeom prst=\"rect\">" +
" <a:avLst/>" +
" </a:prstGeom>" +
" </pic:spPr>" +
" </pic:pic>" +
" </a:graphicData>" +
"</a:graphic>";
XmlToken xmlToken = null;
try {
xmlToken = XmlToken.Factory.parse(picXml);
} catch (XmlException xe) {
// xe.printStackTrace();
throw new RuntimeException("图片XML解析失败");
}
inline.set(xmlToken);
inline.setDistT(0);
inline.setDistB(0);
inline.setDistL(0);
inline.setDistR(0);
CTPositiveSize2D extent = inline.addNewExtent();
extent.setCx(width);
extent.setCy(height);
CTNonVisualDrawingProps docPr = inline.addNewDocPr();
docPr.setId(id);
docPr.setName("Picture " + id);
docPr.setDescr("Generated");
}
}
4、导出效果:
注意事项:如果你调整了代码设置数据,但是没生效,把target目录下的class文件删除重启哈就生效了。
============================好用记得点个赞哟!!!==========================