这篇文档手把手教你完成导出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—typeresponse.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刷新到OutPutwriter.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<>();//所有占位符涉及到的runList<XWPFRun> currentRuns = new ArrayList<>();//当前占位符涉及到的runwhile (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<>();//重置listflag = 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文件删除重启哈就生效了。
============================好用记得点个赞哟!!!==========================