poi根据模板导出word文档
POI结构与常用类
Apache POI是Apache软件基金会的开源项目,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。 .NET的开发人员则可以利用NPOI (POI for .NET) 来存取 Microsoft Office文档的功能。
包名称说明
- HSSF提供读写Microsoft Excel XLS格式档案的功能。
- XSSF提供读写Microsoft Excel OOXML XLSX格式档案的功能。
- HWPF提供读写Microsoft Word DOC格式档案的功能。
- HSLF提供读写Microsoft PowerPoint格式档案的功能。
- HDGF提供读Microsoft Visio格式档案的功能。
- HPBF提供读Microsoft Publisher格式档案的功能。
- HSMF提供读Microsoft Outlook格式档案的功能。
测试例子
测试模板:
测试代码:
@Test public void docxExportTest() throws IOException { InputStream is = null; FileOutputStream fos = null; try { //获取docx解析对象 is = new FileInputStream("F:\\document\\咨询服务合同.docx"); XWPFDocument document = new XWPFDocument(is); //组装参数 File seal = new File("F:\\imgtest\\1.jpg"); Map<String, Object> sealMap = new HashMap<String, Object>(); sealMap.put("width", 50); sealMap.put("height", 50); sealMap.put("type", "jpg"); sealMap.put("content", new FileInputStream(seal)); Map<String, Object> contentMap = new HashMap<>(); contentMap.put("part_a", "张三"); contentMap.put("address_a", "青岛市市南区动漫产业园E座"); contentMap.put("legal_person_a", "李四"); contentMap.put("seal", sealMap); //解析替换段落文本对象 XWPFUtil.changeParagraph(document, contentMap); //生成新的word文档 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String fileName = "咨询服务合同" + sdf.format(new Date()) + ".docx"; File file = new File("F://" + fileName); fos = new FileOutputStream(file); document.write(fos); } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { is.close(); } if (fos != null) { fos.close(); } } }
工具类:

package com.m2plat.puhui.utils; import com.alibaba.fastjson.JSON; import org.apache.commons.io.IOUtils; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; import org.apache.poi.xwpf.usermodel.XWPFRun; 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.ClassPathResource; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.Map; /** * Created by xiangzh on 2018/11/1. */ public class XWPFUtils { private static Logger logger = LoggerFactory.getLogger(XWPFUtils.class); /** * 根据模板生成word文档 * @param os * @param tempPath * @param contentMap */ public static void writeTemp(OutputStream os, String tempPath, Map<String, Object> contentMap){ logger.info("---根据模板生成word文档"); logger.info("---模板地址:{}",tempPath); logger.info("---替换内容:{}", JSON.toJSONString(contentMap)); ClassPathResource resource = new ClassPathResource(tempPath); if(resource == null || !resource.exists()){ logger.error("---模板文件不存在,tempPath:{}",tempPath); return; } InputStream is = null; try{ is = resource.getInputStream(); XWPFDocument document = new XWPFDocument(is); XWPFUtils.changeParagraph(document, contentMap); //生成新的word文档 document.write(os); }catch (IOException e){ logger.error("---输出word文档失败,原因:{}",e.getMessage()); }finally { IOUtils.closeQuietly(is); } } /** * 替换段落文本 * * @param document docx解析对象 * @param textMap 需要替换的信息集合 */ public static void changeParagraph(XWPFDocument document, Map<String, Object> textMap) { //获取段落集合 List<XWPFParagraph> paragraphs = document.getParagraphs(); for (XWPFParagraph paragraph : paragraphs) { List<XWPFRun> runs = paragraph.getRuns(); for (XWPFRun run : runs) { String text = run.getText(0); //判断文本是否需要进行替换 if (checkText(text)) { for (Map.Entry<String, Object> entry : textMap.entrySet()) { //匹配模板与替换值 格式${key} String key = "${" + entry.getKey() + "}"; Object value = entry.getValue(); if (text.contains(key)) { if (value instanceof String) { //文字替换 text = text.replace(key, (String) value); } else if (value instanceof Map) { //图片替换 text = text.replace(key, ""); Map picMap = (Map) value; int width = Integer.parseInt(picMap.get("width").toString()); int height = Integer.parseInt(picMap.get("height").toString()); int picType = getPictureType(picMap.get("type").toString()); FileInputStream fis = (FileInputStream) picMap.get("content"); try { String blipId = document.addPictureData(fis, picType); int id = document.getNextPicNameNumber(picType); XWPFUtils.createPicture(id, blipId, width, height, run); } catch (Exception e) { e.printStackTrace(); } } } } //替换模板原来位置 run.setText(text, 0); } } } } /** * @param id * @param blipId * @param width 宽 * @param height 高 //* @param paragraph 段落 */ private static void createPicture(int id, String blipId, int width, int height,XWPFRun xwpfRun) { final int EMU = 9525; width *= EMU; height *= EMU; CTInline inline = xwpfRun.getCTR().addNewDrawing().addNewInline(); //CTInline inline = paragraph.createRun().getCTR().addNewDrawing().addNewInline(); //在遍历run列表的时候,创建新的run有可能会导致报错 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=\"" + blipId + "\" 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>"; inline.addNewGraphic().addNewGraphicData(); XmlToken xmlToken = null; try { xmlToken = XmlToken.Factory.parse(picXml); } catch (XmlException xe) { xe.printStackTrace(); } 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("docx_img_ " + id); docPr.setDescr("docx Picture"); } /** * 判断文本中是否包含$ * * @param text 文本 * @return 包含返回true, 不包含返回false */ private static boolean checkText(String text) { if (text == null || "".equals(text)) { return false; } return text.contains("$"); } /** * 根据图片类型,取得对应的图片类型代码 * * @param picType * @return int */ private static int getPictureType(String picType) { int res = XWPFDocument.PICTURE_TYPE_PICT; if (picType != null) { if (picType.equalsIgnoreCase("png")) { res = XWPFDocument.PICTURE_TYPE_PNG; } else if (picType.equalsIgnoreCase("dib")) { res = XWPFDocument.PICTURE_TYPE_DIB; } else if (picType.equalsIgnoreCase("emf")) { res = XWPFDocument.PICTURE_TYPE_EMF; } else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) { res = XWPFDocument.PICTURE_TYPE_JPEG; } else if (picType.equalsIgnoreCase("wmf")) { res = XWPFDocument.PICTURE_TYPE_WMF; } } return res; } }
测试结果:
注意事项
1.实际开发过程中,模板通常存放在项目工程中,获取模板的代码如下:
String tempPath = "static/exportTemplates/咨询服务合同.docx"; ClassPathResource resource = new ClassPathResource(tempPath); InputStream is = resource.getInputStream(); XWPFDocument document = new XWPFDocument(is);
2.导出文件时如果报错:Failed to read zip entry source,是因为打包编译将文件解压缩导致出问题,解决办法为在pom文件中配置nonFilteredFileExtension:
<plugin> <artifactId>maven-resources-plugin</artifactId> <version>2.6</version> <configuration> <delimiters> <delimiter>@</delimiter> <delimiter>${*}</delimiter> </delimiters> <useDefaultDelimiters>false</useDefaultDelimiters> <encoding>UTF-8</encoding><!-- 指定编码格式,否则在DOS下运行mvn命令时当发生文件资源copy时将使用系统默认使用GBK编码 --> <nonFilteredFileExtensions> <nonFilteredFileExtension>bar</nonFilteredFileExtension> <nonFilteredFileExtension>zip</nonFilteredFileExtension> <nonFilteredFileExtension>txt</nonFilteredFileExtension> <nonFilteredFileExtension>pdf</nonFilteredFileExtension> <nonFilteredFileExtension>ttf</nonFilteredFileExtension> <nonFilteredFileExtension>xlsx</nonFilteredFileExtension> <nonFilteredFileExtension>xls</nonFilteredFileExtension> <nonFilteredFileExtension>docx</nonFilteredFileExtension> <nonFilteredFileExtension>doc</nonFilteredFileExtension> </nonFilteredFileExtensions> </configuration> </plugin>
3.所有导出参数必须转换成string类型后才能导出,否则替换不了。
参考:
war在服务器上读取文件报:java.io.IOException: Failed to read zip entry source
SpringBoot打成jar包后,读取resources目录下的文件
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步