一、实现功能
1、支持表格、图像、插入页脚,其中图像包含base64 位和 只传入图像路径
2、要求使用对应的模板 故只能作为参考
3、里面的以下判断时根据 模板文件里面的key进行的特殊处理
二、代码(版本 3.15)
package com.hyzs.szcg.doc.utils;
import java.awt.image.BufferedImage;
import java.io.*;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.hyzs.gz.common.core.exception.CommonException;
import com.hyzs.szcg.doc.vo.DocWordUtilVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.model.XWPFHeaderFooterPolicy;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.impl.xb.xmlschema.SpaceAttribute;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import sun.misc.BASE64Decoder;
import javax.imageio.ImageIO;
/**
*
* @Title: WordExport.java
* @Description: 导出word文档
* @author zzh
* @version 1.0
*/
@Slf4j
@Component
public class PoiExportWord {
@Value("${apiKey}")
private String apiKey;
private String templatePath;
private XWPFDocument doc = null;
private FileInputStream is = null;
private OutputStream os = null;
private DocWordUtilVO docWordUtilVO=new DocWordUtilVO();
public DocWordUtilVO init(Map<String,Object> params,String templatePath,String targetPath) throws IOException {
docWordUtilVO.setWordFlag(false);
this.templatePath = templatePath;
File file=new File(this.templatePath);
File fileTarget=new File(targetPath);
if (!fileTarget.getParentFile().exists()){
//目录不存在则创建目录
fileTarget.getParentFile().mkdirs();
}
//插入当前时间 年月日
insertDate(params);
if(file.exists()){
is = new FileInputStream(file);
doc = new XWPFDocument(is);
try {
if(export(params)){
docWordUtilVO.setWordFlag(generate(targetPath));
docWordUtilVO.setWordPath(targetPath);
}
} catch (Exception e) {
throw CommonException.exception("文书生成失败!",e);
}
}else{
// log.error("io error -> 文件模板不存在》"+this.templatePath);
throw CommonException.exception("io error 模板文件不存在");
}
return docWordUtilVO;
}
/**
* 替换掉占位符
* @param params
* @return
* @throws Exception
*/
public boolean export(Map<String,Object> params) throws Exception{
this.replaceInPara(doc, params);
export(params, 0);//表格数据
return true;
}
/**
* 替换掉表格中的占位符
* @param params
* @param tableIndex
* @return
* @throws Exception
*/
public boolean export(Map<String,Object> params,int tableIndex) throws Exception{
//动态插入表格列表数据
this.insertTable(params);
this.replaceInTable(doc, params,tableIndex);
return true;
}
/**
* 循环生成表格
* @param params
* @return
* @throws Exception
*/
public boolean insertTable(Map<String,Object> params) throws Exception{
this.insertValueToTable(doc, params);
return true;
}
private void insertValueToTable(XWPFDocument doc,Map<String, Object> params) throws Exception {
List<XWPFTable> tableList = doc.getTables();
for (int t=0;t<tableList.size();t++){
//循环表格
XWPFTable table = tableList.get(t);
List<XWPFTableRow> rows = table.getRows();
int temRowNum=0;
String key="";//表格 列表数据的 key
for (int j = 0; j <rows.size() ; j++) {
String rowText = rows.get(j).getCell(0).getText();// 获取到表格行的第一个单元格
//确定标识行的位置 标识行的下一行就是模板行 模板行 以 foreachRows_开头 列 ${foreachRows_giveDocList} giveDocList为表单的key
boolean begin = rowText.indexOf("$")>-1;
boolean end = rowText.indexOf("}")>-1;
if(begin&&end){
Matcher matcher = this.matcher(rowText);
if(matcher.find()){
String keyText = matcher.group().substring(2,matcher.group().length()-1);
if(keyText!=null&&!"".equals(keyText)&&keyText.startsWith("foreachRows_")){
temRowNum=j;
key=StringUtils.removeStart(keyText,"foreachRows_");
break;
}
}
}
}
if(rows.size()<2){
break;
}
if(temRowNum==0&&"".equals(key)){
//该表格不需要动态生成
break;
}
//模板行
XWPFTableRow tmpRow = rows.get(temRowNum+1);
//获取列表数据
Object value=params.get(key);
List<Map<String,Object>> listData=new ArrayList<>();
if(value instanceof List){
listData=(List<Map<String,Object>>)value;
}
Map m=new HashMap();
m.putAll(params);
m.remove(key);
for (int i = 0; i < listData.size(); i++) {
Map<String,Object> map = listData.get(i);
map.putAll(m);
map.put("no",i+1);//添加表格序号
//------------------------------------
/* 循环生成模板行 */
//在表格指定位置新增一行
XWPFTableRow targetRow = table.insertNewTableRow(temRowNum+2+i);
copyRow(targetRow,tmpRow);
List<XWPFTableCell> cells;
List<XWPFParagraph> paras;
cells = targetRow.getTableCells();
for (XWPFTableCell cell : cells) {
paras = cell.getParagraphs();
for (XWPFParagraph para : paras) {
this.replaceInPara(para, map);
}
}
}
table.removeRow(temRowNum);// 删除标识行
table.removeRow(temRowNum);// 删除模版行
}
}
/**
* 生成word文档
* @param outDocPath
* @return
* @throws IOException
*/
public boolean generate(String outDocPath){
try {
createFooter();
os = new FileOutputStream(outDocPath);
doc.write(os);
} catch (FileNotFoundException e) {
throw CommonException.exception("文书生成失败!",e);
}catch (IOException e) {
throw CommonException.exception("文书生成失败!",e);
}finally {
try {
doc.close();
this.close(os);
this.close(is);
} catch (IOException e) {
throw CommonException.exception("文书IO流关闭失败!",e);
}
}
return true;
}
/**
* 替换表格里面的变量
*
* @param doc
* 要替换的文档
* @param params
* 参数
* @throws Exception
*/
private void replaceInTable(XWPFDocument doc, Map<String, Object> params,int tableIndex) throws Exception {
List<XWPFTable> tableList = doc.getTables();
if(tableList.size()>tableIndex){
for (int i=0;i<tableList.size();i++){
XWPFTable table = tableList.get(i);
List<XWPFTableRow> rows;
List<XWPFTableCell> cells;
List<XWPFParagraph> paras;
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);
}
}
}
}
}
}
/**
* 替换段落里面的变量
*
* @param doc
* 要替换的文档
* @param params
* 参数
* @throws Exception
*/
private void replaceInPara(XWPFDocument doc, Map<String, Object> params) throws Exception {
Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
XWPFParagraph para;
while (iterator.hasNext()) {
para = iterator.next();
this.replaceInPara(para, params);
}
}
/**
* 替换段落里面的变量
*
* @param para
* 要替换的段落
* @param params
* 参数
* @throws Exception
* @throws IOException
* @throws
*/
private boolean replaceInPara(XWPFParagraph para, Map<String, Object> params) throws Exception {
boolean data = false;
List<XWPFRun> runs;
//有符合条件的占位符
if (this.matcher(para.getParagraphText()).find()) {
runs = para.getRuns();
data = true;
Map<Integer,String> tempMap = new TreeMap<>();
for (int i = 0; i < runs.size(); i++) {
XWPFRun run = runs.get(i);
String runText = run.toString();
//以"$"开头
boolean begin = runText.indexOf("$")>-1;
boolean end = runText.indexOf("}")>-1;
if(begin && end){
tempMap.put(i, runText);
fillBlock(para, params, tempMap, i);
continue;
}else if(begin && !end){
tempMap.put(i, runText);
continue;
}else if(!begin && end){
tempMap.put(i, runText);
fillBlock(para, params, tempMap, i);
continue;
}else{
if(tempMap.size()>0){
tempMap.put(i, runText);
continue;