一、制作word模版,${xxxx}是一会要替换的内容,最下面的表格是要插入数据,根据是否以$开头来判断是需要替换还是插入数据,
注意如果是需要插入数据,制作的表格模版需要一行空行,也只能有一行空行,原因可以看我代码的逻辑,表格中${header}
和${hearder2}是放入需要替换的图片
二、添加poi所需要的jar包文件,我用的maven对jar包进行管理
<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>
三、由于poi自身bug,会出现图片无法显示问题,这里需要自定义一个类继承XWPFDocument类,接下来使用的都是我们自己创建的这个类来操作word对象,这个
类对XWPFDocument进行了继承,所以不用担心会有什么问题
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("测试"); } }
四、接下来就是导出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(); } } } }
五、最后就是进行测试了,我是用的是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(); } }
六、最后生成的word文档