1. 程式人生 > >【Apache POI】Java Web根據模板匯出word檔案

【Apache POI】Java Web根據模板匯出word檔案

最近工作中遇到一個需求:根據word模板文件匯出word檔案。

查閱了一些資料,發現Apache POI可以實現文件讀寫的功能,於是就研究了一下,總結如下:

POI詳細介紹:

Apache POI是一個開源的Java讀寫Excel、WORD等微軟OLE2元件文件的專案。目前POI已經有了Ruby版本。結構:

  • HSSF - 提供讀寫Microsoft Excel XLS格式檔案的功能。

  • XSSF - 提供讀寫Microsoft Excel OOXML XLSX格式檔案的功能。

  • HWPF - 提供讀寫Microsoft Word DOC97格式檔案的功能。

  • XWPF - 提供讀寫Microsoft Word DOC2003格式檔案的功能。

  • HSLF - 提供讀寫Microsoft PowerPoint格式檔案的功能。

  • HDGF - 提供讀Microsoft Visio格式檔案的功能。

  • HPBF - 提供讀Microsoft Publisher格式檔案的功能。

  • HSMF - 提供讀Microsoft Outlook格式檔案的功能。

Java Web根據模板匯出word檔案

基本實現原理:
1、首先,將模板中需要用到的引數組裝為一個Map集合;
2、然後讀取模板文件,使用POI API解析文件輸入流;
3、將Map集合中的內容寫入模板文件。

功能使用了springMVC的相關功能,點選匯出按鈕,呼叫前臺JS,JS程式碼根據springMVC的註解功能實現對後臺匯出方法的呼叫,controller控制類如下:

import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import com.lmb.word.util.XwpfUtil;

/**
 * Java Web根據模板匯出word檔案(spring MVC controller實現)
 * @author lmb
 * @date 2017-3-14
 *
 */
public class ExportControl {
	
	private static final Logger logger = Logger.getLogger(ExportControl.class);

	/**
	 * 匯出word
	 * @param param
	 * @param request
	 * @param response
	 */
	public void exportWord(String param,HttpServletRequest request,HttpServletResponse response){
		logger.debug("匯出word檔案開始>>>>>>>>>>>>>");
		Map<String,Object> params = packageObject();
		/*params格式如下:
		{
			${interfacetype11}=ecsClient, ${time1}=2017/03/14 14:36:15,${code2}=4114030153,  ${interfacetype10}=ecsClient, ${percentagecode10}=6.43%, ${percentagecode11}=17.74%, 
			${text9}=返回欠費不能辦理, ${percentagecode8}=67.71%, ${mainpercentage1}=98.57%, ${code1}=2107000024, ${svcname8}=GUSERBRAND,  ${text8}=2G使用者主產品不在省份上報的列表中, 
			${start1}=2017-03-13 ,  ${svcname9}=GUSERBRAND, ${percentagecode2}=18.06%, ${text10}=使用者狀態不處於有效期,  ${percentagecode1}=78.39%, ${text2}=4114030153, 
			${interfacename1}=cu.tran.fusionflowquery, ${svctext1}=3G流量包查詢, ${svctext2}=3G流量包查詢,  ${svctext8}=使用者品牌查詢,${svctext9}=使用者品牌查詢,${svctext10}=使用者品牌查詢, ${svctext11}=使用者品牌查詢,
			${text11}=4114030153, ${interfacetype8}=ecsClient,  ${svcname11}=GUSERBRAND, ${end1}=2017-03-13 ,  ${interfacename10}=cu.tran.fusionflowquery, ${interfacetype9}=ecsClient,   ${svcname10}=GUSERBRAND, ${text1}=2G使用者主產品不在省份上報的列表中, ${interfacename2}=cu.tran.fusionflowquery, ${interfacename11}=cu.tran.fusionflowquery, 
			${code8}=2107000024, ${namelist1}=  1、3G流量包查詢:24.26%  2、使用者品牌查詢:37.52%, 
			${date1}=2017-03-13 ,  ${code9}=2114000061, ${code11}=4114030153,  ${svcname1}=G3GFLUX, ${code10}=2114000066,  ${svcname2}=G3GFLUX, ${interfacetype1}=ecsClient, 
			${percentagecode9}=4.12%, ${interfacetype2}=ecsClient,  ${interfacename9}=cu.tran.fusionflowquery, 
			${indexprovince1}=陝西,  ${interfacename8}=cu.tran.fusionflowquery
		}
		*/
		XwpfUtil xwpfUtil = new XwpfUtil();
		//讀入word模板
		InputStream is = getClass().getClassLoader().getResourceAsStream("wordTemplate.docx");
		xwpfUtil.exportWord(params,is,request,response,xwpfUtil);
		logger.debug("匯出word檔案完成>>>>>>>>>>>>>");
	}

	/**
	 * 組裝word文件中需要顯示資料的集合
	 * @return
	 */
	public Map<String, Object> packageObject() {
		Map<String,Object> params = new HashMap<String,Object>();
		params.put("${date1}", "");//資料查詢時間
		params.put("${time1}", "");//生成檔案時間
		params.put("${indexprovince1}", "");//省份
		//報告時間範圍
		params.put("${mainpercentage1}", "");//省份成功率
		params.put("${start1}", "");//開始時間
		params.put("${end1}", "");
		params.put("${namelist1}", "");
		// ……
		return params;
	}
}

控制器中用到的匯出方法工具類:

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
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;

/**
 * 根據模板匯出word檔案工具類
 * @author lmb
 * @date 2017-3-14
 */
public class XwpfUtil {
	
	private static final Logger logger = Logger.getLogger(XwpfUtil.class);

	/**
	 * 匯出word檔案
	 * @param params
	 * @param is
	 * @param request
	 * @param response
	 * @param xwpfUtil
	 */
	public void exportWord(Map<String, Object> params, InputStream is,
			HttpServletRequest request, HttpServletResponse response,
			XwpfUtil xwpfUtil) {
		
		XWPFDocument doc = new XWPFDocument();
		xwpfUtil.replaceInPara(doc,params);
		xwpfUtil.replaceInTable(doc,params);
		try {
			OutputStream os = response.getOutputStream();
			response.setContentType("application/vnd.ms-excel");
			response.setHeader("Content-disposition","attachment;filename=exportWord.docx");//檔名中文不顯示
			//把構造好的文件寫入輸出流
			doc.write(os);
			//關閉流
			xwpfUtil.close(os);
			xwpfUtil.close(is);
			os.flush();
			os.close();
		} catch (IOException e) {
			logger.error("檔案匯出錯誤");
		}
	}

	/**
	 * 替換word模板文件段落中的變數
	 * @param doc 要替換的文件
	 * @param params 引數
	 */
	public void replaceInPara(XWPFDocument doc, Map<String, Object> params) {
		Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
		XWPFParagraph para;
		while(iterator.hasNext()){
			para = iterator.next();
			this.replaceInPara(para,params);
		}
	}

	/**
	 * 替換段落中的變數
	 * @param para 要替換的段落
	 * @param params 替換引數
	 */
	public void replaceInPara(XWPFParagraph para, Map<String, Object> params) {
		List<XWPFRun> runs;
		if (((Matcher) 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().trim();
				if (StringUtils.isNotBlank(runText)&&'$' == runText.charAt(0)&&'{' == runText.charAt(1)) {
					start = i;
				}
				if (StringUtils.isNotBlank(runText)&&(start != -1)) {
                    str += runText;
                }
				if (StringUtils.isNotBlank(runText)&&'}' == runText.charAt(runText.length() - 1)) {
                    if (start != -1) {
                        end = i;
                        break;
                    }
                }
			}
			for (int i = start; i <= end; i++) {
                para.removeRun(i);
                i--;
                end--;
                System.out.println("remove i="+i);
            }
            if(StringUtils.isBlank(str)){
            	String temp = para.getParagraphText();
        		str = temp.trim().substring(temp.indexOf("${"),temp.indexOf("}")+1);
            }
            for (String key : params.keySet()) {
                if (str.equals(key)) {
                    para.createRun().setText(String.valueOf(params.get(key)) );
                    break;
                }
            }
		}
	}

	/**
	 * 替換word模板文件表格中的變數
	 * @param doc 要替換的文件
	 * @param params 引數
	 */
	public void replaceInTable(XWPFDocument doc, Map<String, Object> params) {
		Iterator<XWPFTable> iterator = doc.getTablesIterator();
		XWPFTable table;
		List<XWPFTableRow> rows;
		List<XWPFTableCell> cells;
		List<XWPFParagraph> paras;
		while (iterator.hasNext()) {
            table = iterator.next();
            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 paragraphText
	 * @return
	 */
	public Object matcher(String str) {
		Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
		Matcher matcher = pattern.matcher(str);
		return null;
	}
	
	/**
	 * 關閉輸入流
	 * @param is
	 */
	public void close(InputStream is) {
		if (is != null) {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
	}

	/**
	 * 關閉輸出流
	 * @param is
	 */
	public void close(OutputStream os) {
		if (os != null) {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
	}
}