POI讀取word時讀取變數分段的全網最強解決方式

2020-08-12 18:46:45

一、poi word關鍵字替換踩過的巨坑

案例:@加盟商}  把關鍵字替換成爲南京海瀚軟體科技有限公司

通過XWPFRun獲取文字時,可能會被word分成了三段 @   加盟商  },這時無法識別一個完整的變數去做替換。

 

二、解決方式

1、不要在word中直接輸入參數,通過在文字中寫好複製貼上過來。

        經過實踐證明某些欄位確實可以,但是還是會出現被分段。(wps去做變數時不分段率高一些)

2、將word另存爲xml檔案,在編輯器中將變數在一個<w:t></w:t>節點裏完整的放在裏面

        這種方式最終結果是可以實現的。但是在xml中找非常費神且容易改錯,且文件量過大的時候,每一個都去改不太實際

3、使用程式碼的方式去做替換

  • 先把變數處理成完整的變數(採用從@開始符拼接到}結束,中間有很多判斷邏輯,看程式碼)
				// 遍歷獲取段落中所有的runs
				List<XWPFRun> runs = paragraph.getRuns();
				// 合併邏輯
				for (int i = 0; i < runs.size(); i++) {
					String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
					if (text0 != null&&text0.contains("@")) {
						int startIndex = text0.lastIndexOf("@");
						int endIndex = 1;
						if (startIndex != -1) {
							endIndex = text0.substring(startIndex).indexOf("}");
						}
						if (endIndex < 0) {
							// 記錄分隔符中間跨越的runs數量,用於字串拼接和替換
							int num = 0;
							int j = i+1 ;
							for (; j < runs.size(); j++) {
								String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
								if (text1 != null && text1.contains("}")) {
									num = j - i;
									break;
								}
							}
							if (num != 0) {
								// num!=0說明找到了@@配對,需要替換
								StringBuilder newText = new StringBuilder();
								for (int s = i; s <= i + num; s++) {
									String text2 = runs.get(s).getText(runs.get(s).getTextPosition());
									String replaceText = text2;
									if(s==i&&text2.contains("@")&&text2.contains("}")) {
										newText.append(text2);
									}
									else if(s==i&&text2.contains("@")) {
										replaceText = text2.substring(0, text2.indexOf("@"));
										newText.append(text2.substring(text2.indexOf("@")));
									}
									else if (text2.contains("}")) {
										replaceText = text2.substring(replaceText.indexOf("}") + 1);
										newText.append(text2.substring(0, text2.indexOf("}") + 1));
									} else {
										replaceText = "";
										newText.append(text2);
									}
									runs.get(s).setText(replaceText, 0);
								}
								runs.get(i).setText(newText.toString(), 0);
								i = i - 1;
							}
						}
					}
				}

 

 

最後附上poi 處理 word,關鍵字替換文字、圖片、插入excel表、轉pdf的完整案例程式碼

  • generateWord(param, data, template, descpath); 替換
  • wordToPDF(shamName, sfileName, toFileName, readname, ht_attach_id); jacob方式worf轉pdf
package com.agilecontrol.fair.ifc.gold.poi.utils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.poi.POIXMLDocument;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.apache.xmlbeans.XmlCursor;
import org.hsqldb.lib.HashMap;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTBorder;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblBorders;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTblWidth;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTTcPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STBorder;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STJc;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STVerticalJc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.support.DaoSupport;

import com.agilecontrol.fair.FairConfig;
import com.agilecontrol.fair.ifc.gold.FtpUtils;
import com.agilecontrol.fair.ifc.gold.poi.model.CustomXWPFDocument;
import com.agilecontrol.fair.ifc.gold.sap.SAPWebServiceClient_FreezeCustomer;
import com.agilecontrol.fair.ifc.gold.utils.ConnUtils;
import com.agilecontrol.nea.core.query.QueryEngine;
import com.agilecontrol.nea.core.query.QueryException;
import com.agilecontrol.nea.util.NDSException;
import com.jacob.activeX.ActiveXComponent;
import com.jacob.com.ComThread;
import com.jacob.com.Dispatch;
import com.jacob.com.Variant;
import com.lowagie.text.Row;

/**
 * @Title: ConnUtils.java
 * @Package com.agilecontrol.fair.ifc.gold.utils
 * @Description: TODO(MD三期附件WORD生成PDF)
 * @author gaigai
 * @date 2020年8月4日
 * @version V1.0
 */
public class GeneratePdfUntils {

	protected static Logger logger = LoggerFactory.getLogger(GeneratePdfUntils.class);
	private static final int wdFormatPDF = 17; // PDF 格式
	QueryEngine engine;

	public boolean generatePdf(JSONArray param, JSONObject data, String template, String descpath, String shamName,
			String sfileName, String toFileName, String readname, int ht_attach_id) throws Exception {
		boolean flag = true;
		try {
			engine = QueryEngine.getInstance();
			generateWord(param, data, template, descpath);
			wordToPDF(shamName, sfileName, toFileName, readname, ht_attach_id);
		} catch (Exception e) {
			// TODO: handle exception
			flag = false;
			logger.debug("【MD三期附件WORD生成PDF錯誤資訊:{}】", e.getMessage());
		} finally {
			// TODO: handle finally clause
		}
		return flag;
	}

	/**
	 * word 先替換變數再生成pdf
	 * 
	 * @param shamName 假名字 @param sfileName 原始檔地址 @param toFileName 複製到哪裏去的地址
	 * @param readname 真名字 @param ht_attach_id 附件id
	 */
	public void wordToPDF(String shamName, String sfileName, String toFileName, String readname, int ht_attach_id)
			throws Exception {
		readname = readname.split("//.")[0];
		logger.debug("啓動 Word...");
		ActiveXComponent app = null;
		String readurl = sfileName + ht_attach_id + '.' + shamName.split("\\.")[1];
		logger.debug("readurl:" + readurl);
		try {
			ComThread.InitSTA(true);
			app = new ActiveXComponent("Word.Application");
			app.setProperty("Visible", false);
			Dispatch docs = app.getProperty("Documents").toDispatch();
			Dispatch doc = Dispatch.call(docs, //
					"Open", //
					readurl, // FileName
					false, // ConfirmConversions
					true // ReadOnly
			).toDispatch();
			if (!FtpUtils.isPortalPathExist(toFileName)) {
				FtpUtils.isChartPathExist(toFileName);
			}
			SimpleDateFormat df = new SimpleDateFormat("yyMMddHHmmss");// 設定日期格式
			String time = df.format(new Date());
			String filename = toFileName + "\\" + shamName.split("\\.")[0] + ".pdf";
			File tofile = new File(filename);
			if (tofile.exists()) {
				tofile.delete();
			}
			Dispatch selection = Dispatch.get(app, "Selection").toDispatch();// 選定範圍或者插入點
			long start = System.currentTimeMillis();
			Dispatch.call(doc, "SaveAs", filename, // FileName
					wdFormatPDF);
			String targetPath = toFileName + "\\" + "attachment.properties";
			Properties prop = new Properties();
			String propertiesTemplatePath = FairConfig.PROJECT_ADDRESS + "\\" + "attachment.properties";
			// 複製檔案到建立地址然後修改內容
			FtpUtils.copyFile(propertiesTemplatePath, targetPath);
			FileInputStream fis = new FileInputStream(targetPath);
			prop.load(fis);
			fis.close();
			prop.setProperty(shamName.split("\\.")[0] + ".filename", readname.split("\\.")[0] + ".pdf");
			prop.setProperty("ext", "pdf");
			FileOutputStream foss = new FileOutputStream(targetPath);
			prop.store(foss, "Copyright (c) Boxcode Studio");
			String[] splitFileId = toFileName.split("\\\\");
			String updateFileName = "UPDATE m_htattach SET file_fill ='" + readname.split("\\.")[0] + time + ".pdf"
					+ "'  WHERE id = " + ht_attach_id;
			int sus = engine.executeUpdate(updateFileName);
			long end = System.currentTimeMillis();
			Dispatch.call(doc, "Close", false);
		} catch (Exception e) {
			logger.debug("========Error:文件轉換失敗:" + e.getMessage());
			throw new Exception(e);
		} finally {
			File tofile = new File(readurl);
			if (tofile.exists()) {
				tofile.delete();
			}
			if (app != null)
				app.invoke("Quit", new Variant[] {});
			// 如果沒有這句話,winword.exe進程將不會關閉
			ComThread.Release();
		}
	}

	public static boolean generateWord(JSONArray param, JSONObject data, String template, String descpath) {
		CustomXWPFDocument doc = null;
		boolean flag = false;
		try {
			OPCPackage pack = POIXMLDocument.openPackage(template);
			doc = new CustomXWPFDocument(pack);
			if (param != null && param.length() > 0) {
				// 處理段落
				List<XWPFParagraph> paragraphList = doc.getParagraphs();
				processParagraphs(paragraphList, param, data, doc);
				// 處理表格
				Iterator<XWPFTable> it = doc.getTablesIterator();
				while (it.hasNext()) {
					XWPFTable table = it.next();
					List<XWPFTableRow> rows = table.getRows();
					for (XWPFTableRow row : rows) {
						List<XWPFTableCell> cells = row.getTableCells();
						for (XWPFTableCell cell : cells) {
							List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
							processParagraphs(paragraphListTable, param, data, doc);
						}
					}
				}
			}
			FileOutputStream fopts = new FileOutputStream(descpath);
			doc.write(fopts);
			fopts.close();
			flag = true;
		} catch (Exception e) {
			flag = false;
			e.printStackTrace();
		}
		return flag;
	}



	public static void processParagraphs(List<XWPFParagraph> paragraphList, JSONArray map, JSONObject data,
			CustomXWPFDocument doc) throws JSONException, Exception {
		if (paragraphList != null && paragraphList.size() > 0) {
			for (XWPFParagraph paragraph : paragraphList) {
				// 遍歷獲取段落中所有的runs
				List<XWPFRun> runs = paragraph.getRuns();
				// 合併邏輯
				for (int i = 0; i < runs.size(); i++) {
					String text0 = runs.get(i).getText(runs.get(i).getTextPosition());
					if (text0 != null&&text0.contains("@")) {
						int startIndex = text0.lastIndexOf("@");
						int endIndex = 1;
						if (startIndex != -1) {
							endIndex = text0.substring(startIndex).indexOf("}");
						}
						if (endIndex < 0) {
							// 記錄分隔符中間跨越的runs數量,用於字串拼接和替換
							int num = 0;
							int j = i+1 ;
							for (; j < runs.size(); j++) {
								String text1 = runs.get(j).getText(runs.get(j).getTextPosition());
								if (text1 != null && text1.contains("}")) {
									num = j - i;
									break;
								}
							}
							if (num != 0) {
								// num!=0說明找到了@@配對,需要替換
								StringBuilder newText = new StringBuilder();
								for (int s = i; s <= i + num; s++) {
									String text2 = runs.get(s).getText(runs.get(s).getTextPosition());
									String replaceText = text2;
									if(s==i&&text2.contains("@")&&text2.contains("}")) {
										newText.append(text2);
									}
									else if(s==i&&text2.contains("@")) {
										replaceText = text2.substring(0, text2.indexOf("@"));
										newText.append(text2.substring(text2.indexOf("@")));
									}
									else if (text2.contains("}")) {
										replaceText = text2.substring(replaceText.indexOf("}") + 1);
										newText.append(text2.substring(0, text2.indexOf("}") + 1));
									} else {
										replaceText = "";
										newText.append(text2);
									}
									runs.get(s).setText(replaceText, 0);
								}
								runs.get(i).setText(newText.toString(), 0);
								i = i - 1;
							}
						}
					}
				}
				String content = paragraph.getParagraphText();
				// 從第一個@符號到第二個@做拼接
				for (int j = 0; j < runs.size(); j++) {
					String text = runs.get(j).getText(runs.get(j).getTextPosition());
//					logger.debug(text);
					boolean isSetText = false;
					if (text != null) {
						for (int i = 0; i < map.length(); i++) {
							String key = map.getJSONObject(i).optString("paraname");
							String colcode = map.getJSONObject(i).optString("colcode").toLowerCase();
							// 防止@格式不對
							if (text.indexOf(key) >= 0) {
								isSetText = true;
								String value = data.optString(colcode);
								if (key.contains("URL")) {// 圖片替換
									text = text.replace(key, "");
									if (value == null || value.length() == 0) {
										continue;
									}
									int width = 220;
									int height = 150;
									int picType = getPictureType("PNG");
									String getTrueFileName = FtpUtils
											.getTrueFileName(FairConfig.PROJECT_ADDRESS + value);
									if (getTrueFileName.length() == 0 || getTrueFileName == null) {
										continue;
									}
									String url = FairConfig.PROJECT_ADDRESS + value + File.separatorChar
											+ FtpUtils.getShamName(getTrueFileName, FairConfig.PROJECT_ADDRESS + value);
									byte[] byteArray = (byte[]) WordReplaceUtil.inputStream2ByteArray(new FileInputStream(url),
											true);
									ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
									try {
										doc.addPicture(byteArray, picType);
										doc.createPicture(doc.getAllPictures().size() - 1, width, height, paragraph);
									} catch (Exception e) {
										e.printStackTrace();
									}
								} else if (key.contains("XLS")) {
									isSetText = true;
									text = text.replace(key, "");
									XmlCursor cursor = paragraph.getCTP().newCursor();
									// 在指定遊標位置插入表格
									XWPFTable table = doc.insertNewTbl(cursor);
									if (table == null) {
										continue;
									}
									JSONArray dataArray = data.optJSONArray(key);
									if (dataArray != null && dataArray.length() > 0) {
										inserInfo(table, dataArray);
									}
								} else {// 文字替換
									text = text.replace(key, value);
									isSetText = true;
								}
							}
						}
						if (isSetText) {
							runs.get(j).setText(text, 0);
						}
					}

				}

			}
		}
//		if (paragraphList != null && paragraphList.size() > 0) {
//			for (XWPFParagraph paragraph1 : paragraphList) {
//				// 遍歷獲取段落中所有的runs
//				List<XWPFRun> runs1 = paragraph1.getRuns();
//				// 合併邏輯
//				for (int i = 0; i < runs1.size(); i++) {
//					String text = runs1.get(i).getText(runs1.get(i).getTextPosition());
//					logger.debug("替換後的文字:"+text);
//
//				}
//			}
//		}
	}

	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;
	}

	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) {
					System.out.println("關閉流失敗");
				}
			}
		}
		return byteArray;
	}


	/**
	 * 把資訊插入表格
	 * @param table
	 * @param data
	 */
	public static void inserInfo(XWPFTable table, JSONArray dataArray) {
		System.out.println("【生成表格開始】");
		// 表格屬性
		CTTblPr tablePr = table.getCTTbl().addNewTblPr();
		CTTblBorders borders = tablePr.addNewTblBorders();
		CTBorder hBorder = borders.addNewInsideH();
		hBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
		hBorder.setSz(new BigInteger("1")); // 線條大小
		hBorder.setColor("000000"); // 設定顏色

		CTBorder wBorder = borders.addNewInsideV();
		wBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
		wBorder.setSz(new BigInteger("1")); // 線條大小
		wBorder.setColor("000000"); // 設定顏色

		CTBorder bBorder = borders.addNewBottom();
		bBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
		bBorder.setSz(new BigInteger("1")); // 線條大小
		bBorder.setColor("000000"); // 設定顏色

		CTBorder tBorder = borders.addNewTop();
		tBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
		tBorder.setSz(new BigInteger("1")); // 線條大小
		tBorder.setColor("000000"); // 設定顏色

		CTBorder lBorder = borders.addNewLeft();
		lBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
		lBorder.setSz(new BigInteger("1")); // 線條大小
		lBorder.setColor("000000"); // 設定顏色

		CTBorder rBorder = borders.addNewRight();
		rBorder.setVal(STBorder.Enum.forString("single")); // 線條型別
		rBorder.setSz(new BigInteger("1")); // 線條大小
		rBorder.setColor("000000"); // 設定顏色

		JSONObject obj = dataArray.optJSONObject(0);
		XWPFTableRow firstrow = table.createRow();
		XWPFTableCell firstcell = null;
		String cols[] = JSONObject.getNames(obj);
		for (int j = 0; j < cols.length; j++) {
			// 第一行建立了多少列,後續增加的行自動增加列
			String key = cols[j];
			firstcell = firstrow.addNewTableCell();
			firstcell.setText(key);
			CTTc cttc = firstcell.getCTTc();
			CTTcPr ctPr = cttc.addNewTcPr();
			ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
			ctPr.addNewTcW().setW(BigInteger.valueOf(3000));
			cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
		}

		XWPFTableRow row = null;
		XWPFTableCell cell = null;
		for (int i = 0; i < dataArray.length(); i++) {
			JSONObject data = dataArray.optJSONObject(i);
			row = table.createRow();
			for (int j = 0; j < cols.length; j++) {
				cell = row.getCell(j);
				cell.setText(data.optString(cols[j]));
				// 設定水平居中,需要ooxml-schemas包支援
				CTTc cttc = cell.getCTTc();
				CTTcPr ctPr = cttc.addNewTcPr();
				ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
				ctPr.addNewTcW().setW(BigInteger.valueOf(3000));
				cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
			}
		}

	}

}