1. 程式人生 > >POI匯出word,增強方案

POI匯出word,增強方案

poi匯出word方案

poi匯出word的常規使用方式

先看看常規使用poi生成word的一些方法

// 建立文件物件
XWPFDocument docxDocument = new XWPFDocument(); // 建立段落物件 XWPFParagraph paragraphX = docxDocument.createParagraph(); // 建立文字物件 XWPFRun runX = paragraphX.createRun();

設計思路

主要是思路是將某個模板(自己定義的)上傳至伺服器,然後解析成對應的ftl檔案,之後服務在做報告時,就可以直接拿取對應的ftl檔案來生成報告。這種方式會比較靈活,現在用的也比較多。但是對於一份簡單的,不需要ftl的檔案來作為中間生成檔案時,那麼可能會比較難了。比如,接下來我們要學習的,使用word的模板作為報告模板,然後直接生成需要的報告。這個思路大概是:使用者直接設計一個word模板,然後上傳至伺服器,伺服器將其直接儲存到對應的資料夾下;在要生成報告時,直接拿word的模板來填充資料。這樣,好像也不是很難,但是想想細節上的地方,可能很多東西就不好做了。

問題所在

我們在使用word作為一份模板時,第一步要解決的就是向模板中定義的欄位替換成自己要填充的資料,如:${name}要換成自己的名字;其次就是word中也有表格,如何向word上直接畫出表格;再次,表格的行列要怎麼合併; 這些都是我們開發中會遇到的問題。

程式碼演示

public void replaceParagraph(XWPFParagraph xWPFParagraph, Map<String, Object> parametersMap) {
        List<XWPFRun> runs = xWPFParagraph.getRuns
(); String xWPFParagraphText = xWPFParagraph.getText(); //正則匹配字串{****} String regEx = "\\{.+?\\}"; Pattern pattern = Pattern.compile(regEx); Matcher matcher = pattern.matcher(xWPFParagraphText); if (matcher.find()) { // 查詢到有標籤才執行替換 int beginRunIndex = xWPFParagraph.searchText("{", new PositionInParagraph()).getBeginRun();// 標籤開始run位置 int endRunIndex = xWPFParagraph.searchText("}", new PositionInParagraph()).getEndRun();// 結束標籤 StringBuffer key = new StringBuffer(); if (beginRunIndex == endRunIndex) { // {**}在一個run標籤內 XWPFRun beginRun = runs.get(beginRunIndex); String beginRunText = beginRun.text(); int beginIndex = beginRunText.indexOf("{"); int endIndex = beginRunText.indexOf("}"); int length = beginRunText.length(); if (beginIndex == 0 && endIndex == length - 1) { // 該run標籤只有{**} XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex); insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr()); // 設定文字 key.append(beginRunText.substring(1, endIndex)); insertNewRun.setText(getValueBykey(key.toString(), parametersMap)); xWPFParagraph.removeRun(beginRunIndex + 1); } else { // 該run標籤為**{**}** 或者 **{**} 或者{**}**,替換key後,還需要加上原始key前後的文字 XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex); insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr()); // 設定文字 key.append(beginRunText.substring(beginRunText.indexOf("{") + 1, beginRunText.indexOf("}"))); String textString = beginRunText.substring(0, beginIndex) + getValueBykey(key.toString(), parametersMap) + beginRunText.substring(endIndex + 1); insertNewRun.setText(textString); xWPFParagraph.removeRun(beginRunIndex + 1); } } else { // {**}被分成多個run // 先處理起始run標籤,取得第一個{key}值 XWPFRun beginRun = runs.get(beginRunIndex); String beginRunText = beginRun.text(); int beginIndex = beginRunText.indexOf("{"); if (beginRunText.length() > 1) { key.append(beginRunText.substring(beginIndex + 1)); } ArrayList<Integer> removeRunList = new ArrayList<Integer>();//需要移除的run // 處理中間的run for (int i = beginRunIndex + 1; i < endRunIndex; i++) { XWPFRun run = runs.get(i); String runText = run.text(); key.append(runText); removeRunList.add(i); } // 獲取endRun中的key值 XWPFRun endRun = runs.get(endRunIndex); String endRunText = endRun.text(); int endIndex = endRunText.indexOf("}"); //run中**}或者**}** if (endRunText.length() > 1 && endIndex != 0) { key.append(endRunText.substring(0, endIndex)); } // 取得key值後替換標籤 // 先處理開始標籤 if (beginRunText.length() == 2) { // run標籤內文字{ XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex); insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr()); // 設定文字 insertNewRun.setText(getValueBykey(key.toString(), parametersMap)); xWPFParagraph.removeRun(beginRunIndex + 1);//移除原始的run } else { // 該run標籤為**{**或者 {** ,替換key後,還需要加上原始key前的文字 XWPFRun insertNewRun = xWPFParagraph.insertNewRun(beginRunIndex); insertNewRun.getCTR().setRPr(beginRun.getCTR().getRPr()); // 設定文字 String textString = beginRunText.substring(0, beginRunText.indexOf("{")) + getValueBykey(key.toString(), parametersMap); // 分行處理 有#的時候分行(給一個標記) if (textString.contains("#")) { String[] textStrings = textString.split("#"); for (int i = 0; i < textStrings.length; i++) { insertNewRun.setText(textStrings[i]); insertNewRun.addBreak();// 換行 } } else { insertNewRun.setText(textString); } xWPFParagraph.removeRun(beginRunIndex + 1);//移除原始的run } // 處理結束標籤 if (endRunText.length() == 1) { // run標籤內文字只有} XWPFRun insertNewRun = xWPFParagraph.insertNewRun(endRunIndex); insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr()); // 設定文字 insertNewRun.setText(""); xWPFParagraph.removeRun(endRunIndex + 1);//移除原始的run } else { // 該run標籤為**}**或者 }** 或者**},替換key後,還需要加上原始key後的文字 XWPFRun insertNewRun = xWPFParagraph.insertNewRun(endRunIndex); insertNewRun.getCTR().setRPr(endRun.getCTR().getRPr()); // 設定文字 String textString = endRunText.substring(endRunText.indexOf("}") + 1); insertNewRun.setText(textString); xWPFParagraph.removeRun(endRunIndex + 1);//移除原始的run } // 處理中間的run標籤 for (int i = 0; i < removeRunList.size(); i++) { XWPFRun xWPFRun = runs.get(removeRunList.get(i));//原始run XWPFRun insertNewRun = xWPFParagraph.insertNewRun(removeRunList.get(i)); insertNewRun.getCTR().setRPr(xWPFRun.getCTR().getRPr()); insertNewRun.setText(""); xWPFParagraph.removeRun(removeRunList.get(i) + 1);//移除原始的run } }// 處理${**}被分成多個run replaceParagraph(xWPFParagraph, parametersMap); } }

poi-tl 處理word

表格的操作方式和上面的程式碼也是大同小異,也是要寫一個標籤,才能知道哪裡需要迴圈,從上面的程式碼就可以看出用原生poi操作word顯得非常複雜,這個時候我就要給大家推薦一種新的poi的操作, poi-template-language.
模板和外掛構建了整個Poi-tl 的核心。
基於Apache POI進行了一些增強封裝,如合併多個Word文件、合併單元格、圖片處理等,外掛機制使得可以基於模板引擎特性擴展出更豐富的功能。

<dependency>
  <groupId>com.deepoove</groupId>
  <artifactId>poi-tl</artifactId>
  <version>1.4.2</version>
</dependency>

2分鐘快速入門

從一個超級簡單的例子開始:把{{title}}替換成"Poi-tl 模板引擎"。

  1. 新建文件template.docx,包含文字{{title}}
  2. TDO模式:Template + data-model = output
// 核心API採用了極簡設計,只需要一行程式碼
XWPFTemplate template = XWPFTemplate.compile("~/template.docx").render(new HashMap<String, Object>(){{
        put("title", "Poi-tl 模板引擎");
}});
FileOutputStream out = new FileOutputStream("out_template.docx");
template.write(out);
out.flush();
out.close();
template.close();

基本語法

所有的語法結構都是以 {{ 開始,以 }} 結束。

文字模板 {{var}}

TextRenderDataString資料模型,繼承模板樣式的同時,也可以自定義顏色、字型等樣式。

Map<String, Object> datas = new HashMap<String, Object>();
datas.put("author", new TextRenderData("00FF00", "Sayi卅一"));
datas.put("introduce", "http://www.deepoove.com");

圖片模板 {{@var}}

//本地圖片
put("localPicture", new PictureRenderData(120, 120, "src/test/resources/sayi.png"));
//本地圖片byte資料
put("localBytePicture", new PictureRenderData(100, 120, ".png", BytePictureUtils.getLocalByteArray(new File("src/test/resources/logo.png"))));

表格模板 {{#var}}

RowRenderData header = RowRenderData.build(new TextRenderData("FFFFFF", "姓名"), new TextRenderData("FFFFFF", "學歷"));
RowRenderData row = RowRenderData.build("張三", "研究生");
put("table", new MiniTableRenderData(header, Arrays.asList(row)));

列表模板 {{*var}}

put("feature", new NumbericRenderData(new ArrayList<TextRenderData>() {
  {
    add(new TextRenderData("Plug-in grammar, add new grammar by yourself"));
    add(new TextRenderData("Supports word text, header, footer..."));
    add(new TextRenderData("Templates, not just templates, but also style templates"));
  }
}));

文件模板 {{+var}}

DocxRenderData資料模型,支援Word文件的合併,文件模板(重複文件段落)被集合資料迴圈渲染後合併。

List<SegmentData> segments = new ArrayList<SegmentData>();
SegmentData s1 = new SegmentData();
s1.setTitle("經常抱怨的自己");
s1.setContent("每個人生活得都不容易,經常向別人抱怨的人,說白了就是把對方當做“垃圾場”,你一股腦地將自己的埋怨與不滿倒給別人,自己倒是爽了,你有考慮過對方的感受嗎?對方的臉上可能一笑了之,但是心裡可能有一萬隻草泥馬奔騰而過。");
segments.add(s1);

SegmentData s2 = new SegmentData();
s2.setTitle("拖拖拉拉的自己");
s2.setContent("能夠今天做完的事情,不要拖到明天,你的事情沒有任何人有義務去幫你做;不要做“宅男”、不要當“宅女”,放假的日子約上三五好友出去轉轉;經常動手做家務,既能分擔伴侶的負擔,又有一個乾淨舒適的環境何樂而不為呢?");
segments.add(s2);

put("docx_word", new DocxRenderData(new File("~/segment.docx"), segments));

詳細示例

有如下付款通知書模板,我們現在要動態填充內容

/**
 * 付款通知書:表格操作示例
 */
public class PaymentExample {

    PaymentData datas = new PaymentData();

    Style headTextStyle = new Style();
    TableStyle headStyle = new TableStyle();
    TableStyle rowStyle = new TableStyle();

    @Before
    public void init() {
        headTextStyle.setFontFamily("Hei");
        headTextStyle.setFontSize(9);
        headTextStyle.setColor("7F7F7F");

        headStyle.setBackgroundColor("F2F2F2");
        headStyle.setAlign(STJc.CENTER);

        rowStyle = new TableStyle();
        rowStyle.setAlign(STJc.CENTER);


        datas.setNO("KB.6890451");
        datas.setID("ZHANG_SAN_091");
        datas.setTaitou("深圳XX家裝有限公司");
        datas.setConsignee("丙丁");

        datas.setSubtotal("8000");
        datas.setTax("600");
        datas.setTransform("120");
        datas.setOther("250");
        datas.setUnpay("6600");
        datas.setTotal("總共:7200");


        RowRenderData header = RowRenderData.build(new TextRenderData("日期", headTextStyle),
                new TextRenderData("訂單編號", headTextStyle), new TextRenderData("銷售代表", headTextStyle),
                new TextRenderData("離岸價", headTextStyle), new TextRenderData("發貨方式", headTextStyle),
                new TextRenderData("條款", headTextStyle), new TextRenderData("稅號", headTextStyle));
        header.setStyle(headStyle
            
           

相關推薦

POI匯出word,增強方案

poi匯出word方案 poi匯出word的常規使用方式 設計思路 問題所在 程式碼演示 poi-tl 處理word 2分鐘快速入門 基本語法 文字模板 {{var}}

POI匯出word

private static void exportPOI(List<SourceCard> cards ) { //空白文件 XWPFDocument doc =new XWPFDocument(); try { FileOutputStream fos=

java POI匯出Word文件

public class TestPOIWordOut { public static void main(String[] args) { //Blank Document XWPFDocument document= new XWPFDocument();

使用POI匯出Word(含表格)的實現方式及操作Word的工具類

轉載請註明出處:https://www.cnblogs.com/sun-flower1314/p/10128796.html  本篇是關於利用Apache 的POI匯出Word的實現步驟。採用XWPFDocument匯出Word,結構和樣式完全由程式碼控制,操作起來還是非常的不太方便,只能夠建立簡

Java使用POI匯出Word文件

package com.iflytek.chy;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io

POI匯出word設定頁面大小及邊距

/** ==========文件建立====開始======== */ CustomXWPFDocument docx = new CustomXWPFDocument(); // 設定頁面大小間距

POI 匯出 Word 表格

package com.yhksxt.util; import java.io.IOException; import java.math.BigInteger; import javax.servlet.http.HttpServletResponse; import org.apache.poi.

poi-tl匯出word出錯

注意大多數是包依賴缺失 好多教程裡寫的是加入  <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifa

POI根據word模板替換匯出setText內容不能換行問題。

//word模板路徑 String inFile = rootPath+"報告.docx"; //word輸出路徑 String outFile = rootPath + "輸出報告.docx"; XWPFDocument doc; //params 引數 Map<

POI使用word模板檔案迴圈輸出行並匯出word

說明 最近工作被臨時安排了一個給同事們報銷的差事,要知道報銷這個事情對於一個開發人員是真的麻煩透頂,數目金額等等都要核對。。。。想我一個根正苗紅的正宗java開發工程師,是萬萬忍受不了讓我一個個的填寫資訊的,所以就有了這篇文章。 那就開始吧 在這裡我要吐槽

poi匯出word2003(動態資料,不用模板,且生成word能再次匯入)

一、場景需求: 有個專案需要將某個題庫裡面的題目匯出word2003,之前框架裡面沒有匯出word2003的工具類。倒是見過匯出txt,excel的,但是word的沒有。 二、技術選型 網上找了一通,匯出word的有幾個技術方案,不過對於word2003版

目前java匯出word的6種解決方案

最近做的專案,需要將一些資訊匯出到word中。在網上找了好多解決方案,現在將這幾天的總結分享一下。 目前來看,java匯出word大致有6種解決方案:  1:Jacob是Java-COM Bridge的縮寫,它在Java與微軟的COM元件之間構建一座橋樑。使用Jacob自

POI匯出簡單的帶有圖片的Word文件

由於匯出的文件中需要插入圖片,因此需要新建類來處理由於插入圖片引進的錯誤即“匯出的word文件在開啟時會報內容出現錯誤,無法開啟檔案” 新建處理類為: import org.apache.poi.xwpf.usermodel.XWPFDocument; import j

java poi匯出Excel表格超大資料量解決方案

Java實現匯出excel表格功能,大部分都會使用apache poi,apache poi API 地址 POI之前的版本不支援大資料量處理,如果資料過多則經常報OOM錯誤,有時候調整JVM大小效果也不是太好。3.8版本的POI新出來了SXSSFWorkbo

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

最近工作中遇到一個需求:根據word模板文件匯出word檔案。 查閱了一些資料,發現Apache POI可以實現文件讀寫的功能,於是就研究了一下,總結如下: POI詳細介紹: Apache POI是一個開源的Java讀寫Excel、WORD等微軟OLE2元件

POI操作word和html相互轉化

image com 文件 ali cto gpa jar包 nsf fun 下面是裏兩個類:第一個類是html轉為word,第二個是word轉html(最下面附上jar包下載鏈接) package com.wz.poi.wordHtml; /** * 2018/4/24 *

菜鳥調錯——POI匯出Excel報錯No such file or directory

場景重現 Apache POI Linux Tomcat 如上所示,當時在linux+tomcat的環境下,使用apache的poi匯出excel的時候就會報“No such file or directory”的錯誤。 錯誤資訊 java.la

POI匯出excel表格優化

package com.ywj.excel; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java

HSSFWorkbook(poi)匯出excel表格

本文與另一篇文章關聯: csv格式匯出excel報表 其中: String accountDate 入參(日期) AccountInfoEntityResp accountInfoEntityResp 匯出的xml報文內容(轉換成obj物件) xml報文解析見另一篇:x

Java呼叫POI匯出資料庫資訊

資料庫連結包:連結:https://pan.baidu.com/s/18VhAHNoDF-gacq04AnBr4w 密碼:vjlr POI呼叫包:連結:https://pan.baidu.com/s/1Jhz6HlJc9gutRBwWE4KU-g 密碼:dnzq java程式碼: impo