1、製做word模版,${xxxx}是一會要替換的內容,最下面的表格是要插入數據,根據是否以$開頭來判斷是須要替換仍是插入數據,java
注意若是是須要插入數據,製做的表格模版須要一行空行,也只能有一行空行,緣由能夠看我代碼的邏輯,表格中${header}apache
和${hearder2}是放入須要替換的圖片數組
2、添加poi所須要的jar包文件,我用的maven對jar包進行管理app
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.11</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.11</version> </dependency>
3、因爲poi自身bug,會出現圖片沒法顯示問題,這裏須要自定義一個類繼承XWPFDocument類,接下來使用的都是咱們本身建立的這個類來操做word對象,這個框架
類對XWPFDocument進行了繼承,因此不用擔憂會有什麼問題maven
package com.cccuu.project.myUtils; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.xwpf.usermodel.XWPFDocument; import org.apache.poi.xwpf.usermodel.XWPFParagraph; 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 java.io.IOException; import java.io.InputStream; /******************************************* * * @Package com.cccuu.project.myUtils * @Author duan * @Date 2018/3/29 17:55 * @Version V1.0 *******************************************/ public class CustomXWPFDocument extends XWPFDocument { public CustomXWPFDocument(InputStream in) throws IOException { super(in); } public CustomXWPFDocument() { super(); } public CustomXWPFDocument(OPCPackage pkg) throws IOException { super(pkg); } /** * @param id * @param width 寬 * @param height 高 * @param paragraph 段落 */ public void createPicture(int id, int width, int height,XWPFParagraph paragraph) { final int EMU = 9525; width *= EMU; height *= EMU; String blipId = getAllPictures().get(id).getPackageRelationship().getId(); CTInline inline = paragraph.createRun().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=\"" + 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("圖片"+ id); docPr.setDescr("測試"); } }
4、接下來就是導出word的工具類了工具
package com.cccuu.project.myUtils; import org.apache.poi.xwpf.usermodel.*; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; /******************************************* * 經過word模板生成新的word工具類 * @Package com.cccuu.project.myUtils * @Author duan * @Date 2018/3/29 14:24 * @Version V1.0 *******************************************/ public class WordUtils { /** * 根據模板生成word * @param path 模板的路徑 * @param params 須要替換的參數 * @param tableList 須要插入的參數 * @param fileName 生成word文件的文件名 * @param response */ public void getWord(String path, Map<String, Object> params, List<String[]> tableList, String fileName, HttpServletResponse response) throws Exception { File file = new File(path); InputStream is = new FileInputStream(file); CustomXWPFDocument doc = new CustomXWPFDocument(is); this.replaceInPara(doc, params); //替換文本里面的變量 this.replaceInTable(doc, params, tableList); //替換表格裏面的變量 OutputStream os = response.getOutputStream(); response.setHeader("Content-disposition", "attachment; filename=" + fileName); doc.write(os); this.close(os); this.close(is); } /** * 替換段落裏面的變量 * @param doc 要替換的文檔 * @param params 參數 */ private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) { Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator(); XWPFParagraph para; while (iterator.hasNext()) { para = iterator.next(); this.replaceInPara(para, params, doc); } } /** * 替換段落裏面的變量 * * @param para 要替換的段落 * @param params 參數 */ private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) { List<XWPFRun> runs; Matcher matcher; if (this.matcher(para.getParagraphText()).find()) { runs = para.getRuns(); int start = -1; int end = -1; String str = ""; for (int i = 0; i < runs.size(); i++) { XWPFRun run = runs.get(i); String runText = run.toString(); if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) { start = i; } if ((start != -1)) { str += runText; } if ('}' == runText.charAt(runText.length() - 1)) { if (start != -1) { end = i; break; } } } for (int i = start; i <= end; i++) { para.removeRun(i); i--; end--; } for (Map.Entry<String, Object> entry : params.entrySet()) { String key = entry.getKey(); if (str.indexOf(key) != -1) { Object value = entry.getValue(); if (value instanceof String) { str = str.replace(key, value.toString()); para.createRun().setText(str, 0); break; } else if (value instanceof Map) { str = str.replace(key, ""); Map pic = (Map) value; int width = Integer.parseInt(pic.get("width").toString()); int height = Integer.parseInt(pic.get("height").toString()); int picType = getPictureType(pic.get("type").toString()); byte[] byteArray = (byte[]) pic.get("content"); ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray); try { //int ind = doc.addPicture(byteInputStream,picType); //doc.createPicture(ind, width , height,para); doc.addPictureData(byteInputStream, picType); doc.createPicture(doc.getAllPictures().size() - 1, width, height, para); para.createRun().setText(str, 0); break; } catch (Exception e) { e.printStackTrace(); } } } } } } /** * 爲表格插入數據,行數不夠添加新行 * * @param table 須要插入數據的表格 * @param tableList 插入數據集合 */ private static void insertTable(XWPFTable table, List<String[]> tableList) { //建立行,根據須要插入的數據添加新行,不處理表頭 for (int i = 0; i < tableList.size(); i++) { XWPFTableRow row = table.createRow(); } //遍歷表格插入數據 List<XWPFTableRow> rows = table.getRows(); int length = table.getRows().size(); for (int i = 1; i < length - 1; i++) { XWPFTableRow newRow = table.getRow(i); List<XWPFTableCell> cells = newRow.getTableCells(); for (int j = 0; j < cells.size(); j++) { XWPFTableCell cell = cells.get(j); String s = tableList.get(i - 1)[j]; cell.setText(s); } } } /** * 替換表格裏面的變量 * @param doc 要替換的文檔 * @param params 參數 */ private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<String[]> tableList) { Iterator<XWPFTable> iterator = doc.getTablesIterator(); XWPFTable table; List<XWPFTableRow> rows; List<XWPFTableCell> cells; List<XWPFParagraph> paras; while (iterator.hasNext()) { table = iterator.next(); if (table.getRows().size() > 1) { //判斷表格是須要替換仍是須要插入,判斷邏輯有$爲替換,表格無$爲插入 if (this.matcher(table.getText()).find()) { rows = table.getRows(); for (XWPFTableRow row : rows) { cells = row.getTableCells(); for (XWPFTableCell cell : cells) { paras = cell.getParagraphs(); for (XWPFParagraph para : paras) { this.replaceInPara(para, params, doc); } } } } else { insertTable(table, tableList); //插入數據 } } } } /** * 正則匹配字符串 * * @param str * @return */ private Matcher matcher(String str) { Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE); Matcher matcher = pattern.matcher(str); return matcher; } /** * 根據圖片類型,取得對應的圖片類型代碼 * * @param picType * @return int */ private static int getPictureType(String picType) { int res = CustomXWPFDocument.PICTURE_TYPE_PICT; if (picType != null) { if (picType.equalsIgnoreCase("png")) { res = CustomXWPFDocument.PICTURE_TYPE_PNG; } else if (picType.equalsIgnoreCase("dib")) { res = CustomXWPFDocument.PICTURE_TYPE_DIB; } else if (picType.equalsIgnoreCase("emf")) { res = CustomXWPFDocument.PICTURE_TYPE_EMF; } else if (picType.equalsIgnoreCase("jpg") || picType.equalsIgnoreCase("jpeg")) { res = CustomXWPFDocument.PICTURE_TYPE_JPEG; } else if (picType.equalsIgnoreCase("wmf")) { res = CustomXWPFDocument.PICTURE_TYPE_WMF; } } return res; } /** * 將輸入流中的數據寫入字節數組 * * @param in * @return */ public static byte[] inputStream2ByteArray(InputStream in, boolean isClose) { byte[] byteArray = null; try { int total = in.available(); byteArray = new byte[total]; in.read(byteArray); } catch (IOException e) { e.printStackTrace(); } finally { if (isClose) { try { in.close(); } catch (Exception e2) { e2.getStackTrace(); } } } return byteArray; } /** * 關閉輸入流 * * @param is */ private void close(InputStream is) { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 關閉輸出流 * * @param os */ private void close(OutputStream os) { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } }
5、最後就是進行測試了,我是用的是ssm框架,這裏放出測試代碼測試
@RequestMapping("exportWordData") public void exportWordData(HttpServletRequest request,HttpServletResponse response){ WordUtils wordUtil=new WordUtils(); Map<String, Object> params = new HashMap<String, Object>(); params.put("${position}", "java開發"); params.put("${name}", "段然濤"); params.put("${sex}", "男"); params.put("${national}", "漢族"); params.put("${birthday}", "生日"); params.put("${address}", "許昌"); params.put("${height}", "165cm"); params.put("${biYeDate}", "1994-02-03"); params.put("${landscape}", "團員"); params.put("${zhuanYe}", "社會工做"); params.put("${xueLi}", "本科"); params.put("${school}", "江西科技師範大學"); params.put("${phone}", "177"); params.put("${eMail}", "157"); try{ Map<String,Object> header = new HashMap<String, Object>(); header.put("width", 100); header.put("height", 150); header.put("type", "jpg"); header.put("content", WordUtils.inputStream2ByteArray(new FileInputStream("C:/Users/Administrator/Desktop/jar包/11.jpg"), true)); params.put("${header}",header); Map<String,Object> header2 = new HashMap<String, Object>(); header2.put("width", 100); header2.put("height", 150); header2.put("type", "jpg"); header2.put("content", WordUtils.inputStream2ByteArray(new FileInputStream("C:/Users/Administrator/Desktop/jar包/22.jpg"), true)); params.put("${header2}",header2); List<String[]> testList = new ArrayList<String[]>(); testList.add(new String[]{"1","1AA","1BB","1CC"}); testList.add(new String[]{"2","2AA","2BB","2CC"}); testList.add(new String[]{"3","3AA","3BB","3CC"}); testList.add(new String[]{"4","4AA","4BB","4CC"}); String path="C:/Users/Administrator/Desktop/jar包/mobanFile.docx"; //模板文件位置 String fileName= new String("測試文檔.docx".getBytes("UTF-8"),"iso-8859-1"); //生成word文件的文件名 wordUtil.getWord(path,params,testList,fileName,response); }catch(Exception e){ e.printStackTrace(); } }
6、最後生成的word文檔this