使用Freemarker生成Word文件並在文件內新增Echarts圖形報表或迴圈新增表格、圖片資料
一、製作.ftl字尾的word模板檔案
1、新建一個word文件模板
使用其他文字編輯器編寫表示式,如:Editplus
2、將word文件另存為xml並改名為.ftl字尾的檔案
另存完之後關閉word文件,將demo.xml的字尾修改為.ftl,然後使用文字編輯器開啟demo.ftl檔案
3、修改.ftl檔案並生成最終的模板檔案
① 修改圖片的資料內容使用表示式代替
替換之後如下:
② 在資料表格中新增迴圈標籤
二、通過模板檔案生成word文件
1、新增pom.xml的依賴
<dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.23</version> </dependency>
2、index.jsp中新增一個Echarts圖形報表
var option = { angleAxis: { type: 'category', data: ['週一', '週二', '週三', '週四', '週五', '週六', '週日'], z: 10 }, radiusAxis: { }, polar: { }, series: [{ type: 'bar', data: [1, 2, 3, 4, 3, 5, 1], coordinateSystem: 'polar', name: 'A', stack: 'a' }, { type: 'bar', data: [2, 4, 6, 1, 3, 2, 1], coordinateSystem: 'polar', name: 'B', stack: 'a' }, { type: 'bar', data: [1, 2, 3, 4, 1, 2, 5], coordinateSystem: 'polar', name: 'C', stack: 'a' }], legend: { show: true, data: ['A', 'B', 'C'] } }; var myChart = echarts.init(document.getElementById("content")); myChart.setOption(option); //獲取Echart圖形報表生成的Base64編碼格式的資料 var imgData = myChart.getConnectedDataURL(); $.post('/demo/word',{'imgData':imgData},function (data) { alert(data); },'json');
3、後臺處理請求並設定模板資料
@Controller @RequestMapping("/demo") public class DemoController { @RequestMapping("/word") @ResponseBody public String generateWord(String imgData){ // 傳遞過程中 "+" 變為了 " " ,所以需要替換 String newImageInfo = imgData.replaceAll(" ", "+"); // 資料中:data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABI4AAAEsCAYAAAClh/jbAAA ... // 在"base64,"之後的才是圖片資訊 String[] arr = newImageInfo.split("base64,"); //新增模板資料 Map<String,Object> dataMap = new HashMap<>(); dataMap.put("userName","張三"); dataMap.put("imgData",arr[1]); Person person1 = new Person("李四", "男", 36, "18811240001"); Person person2 = new Person("王五", "女", 22, "18811240002"); Person person3 = new Person("趙六", "男", 46, "18811240003"); List<Person> personList = new ArrayList<>(); personList.add(person1); personList.add(person2); personList.add(person3); dataMap.put("personList",personList); //檔案生成路徑 String wordFilePath = "E:\\ftl"; //檔案生成名稱(因為是2003版本的xml模板,這裡使用.doc字尾,如果使用.docx字尾生成的檔案有問題) String wordFileName = "演示文件.doc"; //模板路徑 String templatePath = "E:\\ftl"; //模板檔名稱 String templateFileName = "demo.ftl"; //生成word文件 Boolean result = WordUtil.writeWordReport(wordFilePath, wordFileName, templatePath, templateFileName, dataMap); if(result){ return "success"; }else { return "error"; } } }
4、生成word文件的工具類方法
/**
* 根據freemarker生成word文件並存到指定目錄
* @param wordFilePath word檔案生成的目錄
* @param wordFileName word檔名
* @param templatePath 模板檔案所在的目錄
* @param templateFileName 模板檔名
* @param beanParams 生成word檔案所需要的模板資料
* @return
*/
public static Boolean writeWordReport(String wordFilePath,String wordFileName,
String templatePath,String templateFileName, Map<String, Object> beanParams) {
Configuration config = new Configuration(Configuration.getVersion());
Writer out = null;
try {
config.setDirectoryForTemplateLoading(new File(templatePath));
Template template = config.getTemplate(templateFileName, "UTF-8");
//獲取檔案目錄,如果不存在則建立
String filePath = "";
int index = wordFilePath.lastIndexOf(File.separator);
if(index != wordFilePath.length()-1){
filePath = wordFilePath+ File.separator;
}else {
filePath = wordFilePath;
}
File file1 = new File(filePath);
if(!file1.exists()){
file1.mkdirs();
}
//輸出檔案
File file = new File(filePath+wordFileName);
FileOutputStream fos = new FileOutputStream(file);
out = new OutputStreamWriter(fos, "UTF-8");
template.process(beanParams, out);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}finally{
try {
if(out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、最終生成的word文件如下:
6、補充一:解決傳空值導致程式報空指標異常的問題
如果生成文件時報如下錯誤:
Tip: If the failing expression is known to be legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
這有可能是你在傳值過程中傳了空值,導致模板引擎無法獲取引數值,這時可以將表示式更改為${person.name?default('')}這種形式,這個表示式的意思是如果為空值就替換成'', 這樣即便不小心傳了空值也不會導致程式出錯。
7、補充二:解決匯出圖片缺失或圖片不完整問題
實際開發中會遇到有一些圖形報表匯出圖片時不完整,比如:折線圖匯出時只有點卻沒有線條,這是由於折線圖有動畫,而生成的圖片是在動畫還未結束時就生成的。解決方法就是在配置項中關閉動畫效果。
8、補充三 :解決迴圈新增多張圖片重複問題
按照上面的思路,既然可以迴圈新增資料,那肯定也可以迴圈新增圖片,但是當我們使用迴圈標籤新增圖片時,卻發現生成的多張圖片都是一樣的,這是由於我們沒有修改模板檔案中的圖片標籤的屬性導致的。需要修改的地方有兩個一個是:<w:binData></w:binData>標籤的w:name屬性,一個是:<v:imagedata></v:imagedata>標籤的src屬性。示例如下:
上面這個屬性的含義指的是當前文件的第幾張圖片,如果我們的圖片集合資料從下標0開始則這裡的字尾要修改成"下標+1"的形式。虛擬碼示例如下:
<#list dataList as obj> <!--迴圈開始,obj_index代表當前迴圈物件的索引,從下標0開始-->
<!--虛擬碼-->
<w:binData w:name="${"wordml://0300000"+ obj_index+1 +".png"}" xml:space="preserve">${obj.imgUrl?default('')}</w:binData>
<v:imagedata src="${"wordml://0300000"+ obj_index+1 +".png"}" o:title=""/>
<#/list> <!--迴圈結束-->
如果文件前面已經有一張圖片了,然後才開始迴圈圖片呢?那迴圈開始的圖片就是當前文件的第二張圖片,那字尾就要改成“下標+2”的形式,以此類推。示例如下:
參考: