使用POI导出word文档

步骤一、创建一个需要导出的word文档模板。将表里需要填充的数据对应好要填充的键最后在表的最前方加入一个书签(一个table一个书签)
步骤二、工具类

public class WordExport {

	/** 内部使用的文档对象 **/
	private XWPFDocument document;

	private BookMarks bookMarks = null;

	/**
	 * 	为文档设置模板
	 * @param templatePath  模板文件名称
	 * @throws Exception
	 */
	public void setTemplate(String templatePath) throws Exception {
		try {
			InputStream is=this.getClass().getResourceAsStream(templatePath);
			this.document = new XWPFDocument(OPCPackage.open(is));
			bookMarks = new BookMarks(document);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 进行标签替换的例子,传入的Map中,key表示标签名称,value是替换的信息
	 * @param indicator
	 */
	public void replaceBookMark(Map<String, String> indicator) {
		//循环进行替换
		Iterator<String> bookMarkIter = bookMarks.getNameIterator();
		while (bookMarkIter.hasNext()) {
			String bookMarkName = bookMarkIter.next();

			//得到标签名称
			BookMark bookMark = bookMarks.getBookmark(bookMarkName);

			//进行替换
			if (indicator.get(bookMarkName) != null) {
				bookMark.insertTextAtBookMark(indicator.get(bookMarkName), BookMark.REPLACE);
			}

		}

	}

	public void fillTableAtBookMark(String bookMarkName, List<Map<String, String>> content) {

		//rowNum来比较标签在表格的哪一行
		int rowNum = 0;

		//首先得到标签
		BookMark bookMark = bookMarks.getBookmark(bookMarkName);
		Map<String, String> columnMap = new HashMap<String, String>();
		Map<String, Node> styleNode = new HashMap<String, Node>();

		//标签是否处于表格内
		if (bookMark.isInTable()) {

			//获得标签对应的Table对象和Row对象
			XWPFTable table = bookMark.getContainerTable();
			XWPFTableRow row = bookMark.getContainerTableRow();
//			CTRow ctRow = row.getCtRow();
			List<XWPFTableCell> rowCell = row.getTableCells();
			for (int i = 0; i < rowCell.size(); i++) {
				columnMap.put(i + "", rowCell.get(i).getText().trim());
				//System.out.println(rowCell.get(i).getParagraphs().get(0).createRun().getFontSize());
				//System.out.println(rowCell.get(i).getParagraphs().get(0).getCTP());
				//System.out.println(rowCell.get(i).getParagraphs().get(0).getStyle());

				//获取该单元格段落的xml,得到根节点
				Node node1 = rowCell.get(i).getParagraphs().get(0).getCTP().getDomNode();

				//遍历根节点的所有子节点
				for (int x = 0; x < node1.getChildNodes().getLength(); x++) {
					if (node1.getChildNodes().item(x).getNodeName().equals(BookMark.RUN_NODE_NAME)) {
						Node node2 = node1.getChildNodes().item(x);

						//遍历所有节点为"w:r"的所有自己点,找到节点名为"w:rPr"的节点
						for (int y = 0; y < node2.getChildNodes().getLength(); y++) {
							if (node2.getChildNodes().item(y).getNodeName().endsWith(BookMark.STYLE_NODE_NAME)) {
								//将节点为"w:rPr"的节点(字体格式)存到HashMap中
								styleNode.put(i + "", node2.getChildNodes().item(y));
							}
						}
					} else {
						continue;
					}
				}
			}

			//循环对比,找到该行所处的位置,删除改行			
			for (int i = 0; i < table.getNumberOfRows(); i++) {
				if (table.getRow(i).equals(row)) {
					rowNum = i;
					break;
				}
			}
			table.removeRow(rowNum);
			int rowHeight=table.getRow(0).getHeight();//获取第一行高度
			for (int i = 0; i < content.size(); i++) {
				//创建新的一行,单元格数是表的第一行的单元格数,
				//后面添加数据时,要判断单元格数是否一致
				XWPFTableRow tableRow = table.createRow();
				CTTrPr trPr = tableRow.getCtRow().addNewTrPr();
				CTHeight ht = trPr.addNewTrHeight();
				ht.setVal(BigInteger.valueOf(rowHeight));
			}

			//得到表格行数
			int rcount = table.getNumberOfRows();
			for (int i = rowNum; i < rcount; i++) {
				XWPFTableRow newRow = table.getRow(i);

				//判断newRow的单元格数是不是该书签所在行的单元格数
				if (newRow.getTableCells().size() != rowCell.size()) {

					//计算newRow和书签所在行单元格数差的绝对值
					//如果newRow的单元格数多于书签所在行的单元格数,不能通过此方法来处理,可以通过表格中文本的替换来完成
					//如果newRow的单元格数少于书签所在行的单元格数,要将少的单元格补上
					int sub = Math.abs(newRow.getTableCells().size() - rowCell.size());
					//将缺少的单元格补上
					for (int j = 0; j < sub; j++) {
						newRow.addNewTableCell();
					}
				}

				List<XWPFTableCell> cells = newRow.getTableCells();

				for (int j = 0; j < cells.size(); j++) {
					XWPFParagraph para = cells.get(j).getParagraphs().get(0);
					XWPFRun run = para.createRun();
					if (content.get(i - rowNum).get(columnMap.get(j + "")) != null) {
						//判断传送内容是否为图片
						Object obj = content.get(i - rowNum).get(columnMap.get(j + ""));
		                //判断是图片还是字符串
		                if (obj instanceof String) {
		                	//改变单元格的值,标题栏不用改变单元格的值 
							run.setText(obj.toString());
							//将单元格段落的字体格式设为原来单元格的字体格式
							run.getCTR().getDomNode().insertBefore(styleNode.get(j + "").cloneNode(true), run.getCTR().getDomNode().getFirstChild());
		                } else if (obj instanceof Map) {
		                    Map pic = (Map) obj;
		                    int width = Integer.parseInt(pic.get("width").toString());
		                    int height = Integer.parseInt(pic.get("height").toString());
		                    int picType = WordUtils.getPictureType(pic.get("type").toString());
		                    byte[] byteArray = (byte[]) pic.get("content");
		                    ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
		                    try {
//		                    	File file = new File(path);//TODO path:模板word路径
//		                        InputStream is = new FileInputStream(file);
//		                        CustomXWPFDocument doc = new CustomXWPFDocument(is);
//		                        
//		                    		doc.addPictureData(byteInputStream, picType);
//		                    		doc.createPicture(doc.getAllPictures().size() - 1, width, height, para);
		                    } catch (Exception e) {
		                        e.printStackTrace();
		                    }
		                }
						
					}
					cells.get(j).setVerticalAlignment(XWPFVertAlign.CENTER);	
					para.setAlignment(ParagraphAlignment.CENTER);
				}
			}
		}
	}

	public void replaceText(Map<String, String> bookmarkMap, String bookMarkName) {

		//首先得到标签
		BookMark bookMark = bookMarks.getBookmark(bookMarkName);
		//获得书签标记的表格
		XWPFTable table = bookMark.getContainerTable();
		//获得所有的表
		//Iterator<XWPFTable> it = document.getTablesIterator();

		if (table != null) {
			//得到该表的所有行
			int rcount = table.getNumberOfRows();
			ParagraphAlignment align=null;
			XWPFRun run=null;
			for (int i = 0; i < rcount; i++) {
				XWPFTableRow row = table.getRow(i);
				//获到改行的所有单元格
				List<XWPFTableCell> cells = row.getTableCells();
				for (XWPFTableCell c : cells) {
					for (Entry<String, String> e : bookmarkMap.entrySet()) {
						if (c.getText().equals(e.getKey())) {
							//获取原单元格对齐方式
							align=c.getParagraphs().get(0).getAlignment();
							//删掉单元格内容
							c.removeParagraph(0);
							//添加段落并设置段落文本对齐方式
							c.addParagraph().setAlignment(align);
							//设置字体大小
							run= c.getParagraphs().get(0).createRun();
							run.setFontFamily("宋体");
							c.getParagraphs().get(0).setSpacingBefore(0);
							c.getParagraphs().get(0).setSpacingAfter(0);
							c.setVerticalAlignment(XWPFVertAlign.CENTER);
							c.getParagraphs().get(0).setVerticalAlignment(TextAlignment.CENTER);
//							run.setFontSize(15);
							//给单元格赋值
							run.setText(e.getValue());
						}
					}
					if("governmentNetwork".equals(bookMarkName)) {
						c.getParagraphs().get(0).setSpacingBefore(0);
						c.getParagraphs().get(0).setSpacingAfter(0);
					}
				}
			}
			if("governmentNetwork".equals(bookMarkName))
				table.setWidth(9000);
		}
	}

	public void replaceText(Map<String, String> bookmarkMap, String bookMarkName, WordStyle style) {

		//首先得到标签
		BookMark bookMark = bookMarks.getBookmark(bookMarkName);
		//获得书签标记的表格
		XWPFTable table = bookMark.getContainerTable();
		//获得所有的表
		//Iterator<XWPFTable> it = document.getTablesIterator();

		if (table != null) {
			//得到该表的所有行
			int rcount = table.getNumberOfRows();
			ParagraphAlignment align=null;
			XWPFRun run=null;
			for (int i = 0; i < rcount; i++) {
				XWPFTableRow row = table.getRow(i);
				//获到改行的所有单元格
				List<XWPFTableCell> cells = row.getTableCells();
				for (XWPFTableCell c : cells) {
					for (Entry<String, String> e : bookmarkMap.entrySet()) {
						if (c.getText().equals(e.getKey())) {
							//获取原单元格对齐方式
							align=c.getParagraphs().get(0).getAlignment();
							//删掉单元格内容
							c.removeParagraph(0);
							//添加段落并设置段落文本对齐方式
							c.addParagraph().setAlignment(align);
							//设置字体大小
							run= c.getParagraphs().get(0).createRun();
							run.setFontFamily(style.getFontFamily());
							c.getParagraphs().get(0).setSpacingBefore(0);
							c.getParagraphs().get(0).setSpacingAfter(0);
							c.setVerticalAlignment(XWPFVertAlign.CENTER);
							c.getParagraphs().get(0).setVerticalAlignment(TextAlignment.CENTER);
							run.setFontSize(style.getFontSize());
							//给单元格赋值
							run.setText(e.getValue());
						}
					}
					if("governmentNetwork".equals(bookMarkName)) {
						c.getParagraphs().get(0).setSpacingBefore(0);
						c.getParagraphs().get(0).setSpacingAfter(0);
					}
				}
			}
			if("governmentNetwork".equals(bookMarkName))
				table.setWidth(9000);
		}
	}

	public void saveAs(String fileName) {
		File newFile = new File(fileName);
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(newFile);
			this.document.write(fos);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fos != null){
					fos.flush();
				}
				if (fos != null){
					fos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public void saveAs(HttpServletResponse resp) {
		OutputStream fos = null;
		try {
			fos = resp.getOutputStream();
			this.document.write(fos);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if (fos != null){
					fos.flush();
				}
				if (fos != null){
					fos.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}


	
	
	public XWPFDocument getDocument() {
		return document;
	}

	public void setDocument(XWPFDocument document) {
		this.document = document;
	}

	/**
	 * 	设置行元素样式
	 * @param run 行元素
	 * @param font 字体
	 * @param fontSize 字体大小
	 */
	private void setXWPFRunStyle(XWPFRun run, String font, int fontSize) {
		run.setFontSize(fontSize);
		CTRPr rpr=run.getCTR().isSetRPr()?run.getCTR().getRPr():run.getCTR().addNewRPr();
		CTFonts fonts=rpr.isSetRFonts()?rpr.getRFonts():rpr.addNewRFonts();
		fonts.setAscii(font);
		fonts.setEastAsia(font);
		fonts.setHAnsi(font);
	}
	
	/**
	 * 将所有需导出的秘密事项相关文件的document对象内容合并至第一个document
	 * @param docList 需合并的document
	 * @param type 是否委外会签:1 否 2是
	 * @throws Exception
	 */
	public static void mergeDocument(List<XWPFDocument> docList, String type) throws Exception {
		//获取第一个document对象
		XWPFDocument firstDoc=docList.get(0);
		//如果为委外会签则为第一个文档添加分页符
		if(SecretItemsDetail.DOWNLOAD_FILE_TYPE_OUT.equals(type)) {
			//根据document对象创建段落对象
			XWPFParagraph fPara= firstDoc.createParagraph();
			//为段落对象设置分页符
			fPara.setPageBreak(true);
		}
		//提取第一个document对象的xml文档信息
		CTBody srcBody=firstDoc.getDocument().getBody();
		String xmlBody=srcBody.xmlText();
		//提取第一个document对象的xml文档头部信息
		String prefix=xmlBody.substring(0, xmlBody.indexOf(">")+1);
		//提取第一个document对象的xml文档主体信息
		String mainPart=xmlBody.substring(xmlBody.indexOf(">")+1, xmlBody.lastIndexOf("<"));
		//提取第一个document对象的xml文档尾部信息
		String sufix=xmlBody.substring(xmlBody.lastIndexOf("<"));
		//遍历剩余的document对象
		XWPFParagraph para= null;//初始化段落对象
		XmlOptions options=null;//初始化document对象属性
		CTBody appendBody=null;//初始化文档对象主体信息
		String appendBodyXml=null;//初始化文档对象xml文档信息
		String appendMainPart="";//初始化文档对象xml文档主体信息
		String mergeBodyXml=null;//初始化合并后的文档对象xml信息
		for(int i=1;i<docList.size();i++) {
			if(i!=docList.size()-1) {
				//根据document对象创建段落对象
				para= docList.get(i).createParagraph();
				//为段落对象设置分页符
				para.setPageBreak(true);
			}
			//设置当前的document对象的xmloptions属性
			options=new XmlOptions();
			options.setSaveOuter();
			//获取当前当前的document对象xml文档信息
			appendBody=docList.get(i).getDocument().getBody();
			appendBodyXml=appendBody.xmlText(options);
			//获取当前的document对象主体信息
			appendMainPart+=appendBodyXml.substring(appendBodyXml.indexOf(">")+1, appendBodyXml.lastIndexOf("<"));
		}
		//去掉附加文档中的"w:sectPr"标签
		String regex = "<[\\s]*?w:sectPr[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?w:sectPr[\\s]*?>";//查找"w:sectPr"标签正则
		appendMainPart=appendMainPart.replaceAll(regex, "");
		//拼接第一个document对象的头部信息、主体信息、当前的document对象主体信息和第一个document对象的尾部信息
		mergeBodyXml=prefix+mainPart+appendMainPart+sufix;
		//将拼接后的信息转换为新的主体信息并将其设置为第一个document对象的xml文档主体信息
		srcBody.set(CTBody.Factory.parse(mergeBodyXml));
	}
	
	
	/**
	 * 设置响应输出文件头信息
	 * @param fileName 文件名
	 * @param response 响应对象
	 */
	public static void setResponseHeader(String fileName, HttpServletResponse response) {
    	try {
//    		fileName=new String(fileName.getBytes("gbk"),"iso-8859-1");
			fileName=new String(fileName.getBytes("UTF-8"),"iso-8859-1");
			response.setContentType("application/octet-stream;charset=UTF-8");
			response.setHeader("Content-Disposition", "attachment;filename="+fileName);
			response.addHeader("Pargam", "no-cache");
			response.addHeader("Cache-Control", "no-cache");
			response.setCharacterEncoding("UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
    }
}

步骤三、编写word导出接口

@Transactional(readOnly = false)
public void fileWord(Entry entry, HttpServletResponse response) {
    WordExport wordExport = new WordExport();
    try {
        Map<String, String> content1 = new HashMap<String, String>();
        content1.put("drafter", entry.getName());
        String templeatePath = "word模板路径";
            wordExport.setTemplate(templeatePath);//加载模板
            wordExport.replaceText(content1, "table_1");//根据标签替换字段
            String downloadFileName = entry.getName()+  ".docx";//设置word名称
            WordExport.setResponseHeader(downloadFileName, response);//设置相应文信息
            wordExport.saveAs(response);//下载word

    } catch (Exception e) {
        e.printStackTrace();
    }
}

步骤五、前端调用该接口