之前java總用freemaker進行導出,但是改xml實在是太繁瑣了,這次找了另一個工具進行體驗.
一、簡單導出
pom引入
<dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.core</artifactId><version>2.0.6</version></dependency><dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.document</artifactId><version>2.0.6</version></dependency><dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.template</artifactId><version>2.0.6</version></dependency><dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.document.docx</artifactId><version>2.0.6</version></dependency><dependency><groupId>fr.opensagres.xdocreport</groupId><artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId><version>2.0.6</version></dependency>
導出代碼
public void genDoc() {RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;HttpServletResponse response = attributes.getResponse();try (InputStream inputStream = POICacheManager.getFile("template.docx")) {// 3. 讀取模板,創建 IXDocReport 對象IXDocReport report = XDocReportRegistry.getRegistry().loadReport(inputStream, TemplateEngineKind.Freemarker);// 4. 創建上下文,設置變量(你可以按需擴展)IContext context = report.createContext();context.put("name", "張三");//編輯域代碼 ctrl F9 類別選 郵件合并 域名mergefield 域代碼后面填 ${name}// 設置響應頭,準備輸出 Word 文件response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");response.setHeader("Content-Disposition", "attachment; filename=generated-document.docx");// 5. 生成 Word 并寫入響應輸出流try (OutputStream out = response.getOutputStream()) {report.process(context, out);}} catch (Exception e) {log.error(">>> 生成 Word 失敗:", e);// 手動返回錯誤信息try {response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);response.getWriter().write("生成文檔失敗:" + e.getMessage());} catch (Exception ex) {log.error(">>> 寫回錯誤信息失敗", ex);}}}
在resource目錄新建word文件名為template.docx
打開template.docx
輸入任意字符,然后 Ctrl+9插入域,然后右鍵編輯域,類別選 郵件合并 域名選mergefield 域代碼后面填 ${name} 保存后用上面的代碼導出
導出后可以看到域代碼位置被填充為張三了。
二、進階用法
1.動態表格行
分兩種情況處理
(1)第一列為序號列
將第一列填充3個域??
"@before-row[#list productInfoList as item]"
${item?index+1}
@after-row[/#list]
第二列 填充實際的字段即可
如
${item.name}
(2)第一列不序號列
將上文中的序號表達式${item?index+1}?換成你想填充的表達式,如
${item.code}
其他處理跟上賣弄一樣
在java中的定義,先定義實體,然后context.put即可
//假設之前定義了productInfo實體,實體有code name等字段
List<ProductInfo> productInfoList = new ArrayList();
...context.put("productInfoList", productInfoList );
2.圖片
圖片不用域,在要導入的地方插入一張圖片,然后建立書簽,建立書簽的方法如下
選中文檔中的某張圖片,單擊【插入】菜單下【鏈接】子菜單中的【書簽】。
這里的書簽名跟上面的域變量名類似
java代碼
//根據文件讀取
IImageProvider iImageProvider = new FileImageProvider(file, false);
context.put("pic", iImageProvider);//dataurlbase64的字符串,不含前綴
byte[] imageBytes = Base64.getDecoder().decode(base64Image);
//創建 ByteArrayImageProvider 實例
ByteArrayImageProvider imageProvider = new ByteArrayImageProvider(imageBytes, "png");
context.put("pic", imageProvider );
3.條件顯示隱藏
使用if標簽,下面是3個域
[#if data.ccUser??] 申請人:${data.ccUser } [/#if]
可以看到兩個if標簽域包裹了文字和變量
4.空值處理
空值直接使用會報錯,可使用表達式
${tx.amount!}
或者
${tx.amount?if_exists}
上面表達式表示有值填充,無值不填充
5.單元格合并
實現起來比較復雜,在模板中先合并,然后對多列使用第一節的動態表格行基本上可以實現按層級合并,如果是跨列合并,比較復雜的合并目前還沒找到好的解決方案?或許需要考慮原生freemaker或者poi-tl?
6.關鍵字
在實體中應盡量避免使用以下關鍵字
length?