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目錄下的文件