之前寫過一篇利用Freemarker模板生成doc的博客,不過那個博客有點缺陷,不支持生成docx格式的文檔。所以,這裏補充一篇,生成docx或doc格式的文檔以具體的docx模板或者doc模板爲主。這裏以docx爲例。
具體思路:
- 把docx文檔修改爲ZIP格式(修改.docx後綴名爲.zip)
- 獲取zip裏的document.xml文檔以及_rels文件夾下的document.xml.rels文檔
- 把內容填充到document.xml裏,以及圖片配置信息填充至document.xml.rels文檔裏
- 在輸入docx文檔的時候把填充過內容的的 document.xml、document.xml.rels用流的方式寫入zip(詳見下面代碼)。
- 把圖片寫入zip文件下word/media文件夾中
- 輸出docx文檔
docx模板修改成zip格式後的信息如下(因爲word文檔本身就是ZIP格式實現的)
- document.xml裏存放主要數據
- media存放圖片信息
- _rels裏存放配置信息
注意:如果docx模板裏的圖片帶有具體路徑的話,則圖片的格式不受限制。
如果docx模板裏裏圖片信息不帶路徑,則模板僅支持和模板圖片類型一致的圖片。
交代了這麼多,下面就可以乾貨。
-
準備好docx模板
-
把docx文檔修改爲ZIP格式(修改.docx後綴名爲.zip)
-
獲取zip文件裏的word文件夾下的document.xml以及_rels文件夾裏的document.xml.rels文件作爲模板。
注意:這裏圖片配置信息是根據 rId來獲取的。docx模板總的${img_warn}就是rId的具體值。
爲了避免重複,我的圖片rId從17開始(在我沒有修改之前,裏面最大的rId是rId17)。 -
填充模板信息、寫入圖片信息。
String temp_path = File.separator+"template"+File.separator+"test";
// HttpServletRequest request = new MockHttpServletRequest();
// String base_path = request.getSession().getServletContext().getRealPath(File.separator)+temp_path+File.separator;
// System.out.println(base_path);
Map<String,Object> data_map = new HashMap<>();
// 1) 總體環比分析
List<String> res = new ArrayList<>();
res.add("本週報警總數環比上週上升5.62%,上週報警數20882,本週報警數22055。");
data_map.put("data_all_summary_title",res);
res = new ArrayList<>();
res.add("本週高架總數環比上週上升16.55%,上週報警數2405,本週報警數2803。");
data_map.put("data_all_content_title",res);
res = new ArrayList<>();
res.add("\uF06C\t本週斷面環比上週上升12.8%,上週報警數547,本週報警數617。");
res.add("\uF06C\t本週上匝道環比上週上升17.5%,上週報警數1834,本週報警數2155。");
res.add("\uF06C\t本週下匝道環比上週上升29.17%,上週報警數24,本週報警數31。");
data_map.put("data_all_content",res);
res = new ArrayList<>();
res.add("本週路口環比上週上升4.19%,上週報警數18477,本週報警數19252。");
data_map.put("data_all_end",res);
String output_path = "D:/export/";
String output_file_name = System.currentTimeMillis()+"_freemarker_word.docx";
// FreemarkerWordUtils.createDocx(data_map,file_name,"document.xml",output_path, output_path+file_name);
/**
*
* @param dataMap 參數數據
* @param docxTemplate docx模板名稱
* @param xmltemplateName xml模板名稱
* @param xmltConfigemplateName xml配置主模板名稱 一般用來配置圖片、樣式信息
* @param temp_path 模板存放路徑
* @param output_path 產出路徑
* @param template_rir 模板文件下的子文件夾
* @param output_file_name 產出文件名稱
*/
List<String> picNameList = new ArrayList<>();
picNameList.add("pic1.png");
Map<String,String> picFiles = new HashMap<>();
picFiles.put("pic1.png","D:\\export\\png\\pic1.png");
picNameList.add("pic2.png");
picFiles.put("pic2.png","D:\\export\\png\\pic2.png");
picNameList.add("pic3.png");
picFiles.put("pic3.png","D:\\export\\png\\pic3.png");
picNameList.add("pic4.png");
picFiles.put("pic4.png","D:\\export\\png\\pic4.png");
picNameList.add("pic5.png");
picFiles.put("pic5.png","D:\\export\\png\\pic5.png");
picNameList.add("pic6.png");
picFiles.put("pic6.png","D:\\export\\png\\pic6.png");
picNameList.add("summary_pic7.png");
picFiles.put("summary_pic7.png","D:\\export\\png\\summary_pic7.png");
picNameList.add("summary_pic8.png");
picFiles.put("summary_pic8.png","D:\\export\\png\\summary_pic8.png");
picNameList.add("summary_pic9.png");
picFiles.put("summary_pic9.png","D:\\export\\png\\summary_pic9.png");
data_map.put("picNameList",picNameList);
data_map.put("picFiles",picFiles);
String base_path = "D:\\idea_workspace\\alarm\\alarm\\src\\main\\resources\\template\\test\\";
FreemarkerWordUtils.createDocx(data_map, "ptbjjcxbg.doc", "document.xml","document.xml.rels", temp_path,base_path, output_path, output_file_name);
/**
* 生成主數據模板xml
* @param dataMap 數據參數
* @param templateName 模板名稱
* @param pathPrefix 模板路徑
* @param filePath 生成路徑
*/
public static void createTemplateXml(Map dataMap, String templateName, String pathPrefix ,String filePath){
try {
//創建配置實例
Configuration configuration = new Configuration();
//設置編碼
configuration.setDefaultEncoding("UTF-8");
//ftl模板文件統一放至 com.lun.template 包下面
// configuration.setDirectoryForTemplateLoading(new File("D:/idea_workspace/alarm/alarm/src/main/resources/template/"));
// configuration.setClassForTemplateLoading(FreemarkerWordUtils.class,"/template/doc");
configuration.setClassForTemplateLoading(FreemarkerWordUtils.class,pathPrefix);
//獲取模板
Template template = configuration.getTemplate(templateName);
//輸出文件
File outFile = new File(filePath);
//如果輸出目標文件夾不存在,則創建
if (!outFile.getParentFile().exists()){
outFile.getParentFile().mkdirs();
}
//將模板和數據模型合併生成文件
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
//生成文件
template.process(dataMap, out);
//關閉流
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* @param dataMap 參數數據
* @param docxTemplate docx模主板名稱
* @param xmltemplateName xml主模板名稱
* @param xmltConfigemplateName xml配置主模板名稱 一般用來配置圖片、樣式信息
* @param temp_path 模板存放路徑
* @param base_path 模板實際路徑
* @param output_path 產出路徑
* @param output_file_name 產出文件名稱
*/
public static void createDocx(Map dataMap,String docxTemplate, String xmltemplateName,String xmltConfigemplateName,String temp_path,String base_path,String output_path,String output_file_name){
try {
try {
//================================拼裝生成xml配置文檔================================
String xml_config_output_path = output_path+System.currentTimeMillis()+".xml.rels";
createTemplateXml(dataMap,xmltConfigemplateName,temp_path,xml_config_output_path);
File xmlConfigFile = new File(xml_config_output_path);
//讀取 xmlConfigFile 文件 並獲取rId 與 圖片的關係
String xmlConfigFile_content = getFreemarkerContent(dataMap,xmltConfigemplateName,temp_path);
System.out.println("===========================xmlConfigFile_content================================");
System.out.println(xmlConfigFile_content);
System.out.println("===========================xmlConfigFile_content================================");
Document document = DocumentHelper.parseText(xmlConfigFile_content);
Element rootElt = document.getRootElement(); // 獲取根節點
Iterator iter = rootElt.elementIterator() ;// 獲取根節點下的子節點head
List<String> warn_img_list = new ArrayList<>();
List<String> warn_summary_img_list = new ArrayList<>();
// 遍歷Relationships節點
while (iter.hasNext()) {
Element recordEle = (Element) iter.next();
String id = recordEle.attribute("Id").getData().toString();
String target = recordEle.attribute("Target").getData().toString();
if(target.indexOf("media")==0){
// System.out.println("id>>>"+id+" >>>"+target);
// id>>>rId18 >>>media/pic1
if(target.indexOf("summary")>0){
warn_summary_img_list.add(id);
}else{
warn_img_list.add(id);
}
}
}
dataMap.put("warn_img_list",warn_img_list);
if(!warn_summary_img_list.isEmpty()){
dataMap.put("img_sum_warn_o",warn_summary_img_list.get(0));
dataMap.put("img_sum_warn_t",warn_summary_img_list.get(1));
dataMap.put("img_sum_warn_tt",warn_summary_img_list.get(2));
}
// dataMap.put("","");
//================================拼裝生成xml配置文檔================================
//================================拼裝生成主模板xml文檔================================
String xml_output_path = output_path+System.currentTimeMillis()+".xml";
createTemplateXml(dataMap,xmltemplateName,temp_path,xml_output_path);
File xmlFile = new File(xml_output_path);
//================================拼裝生成主模板xml文檔================================
File docxFile = new File(base_path+docxTemplate);
if(!docxFile.exists()){
docxFile.createNewFile();
}
ZipFile zipFile = new ZipFile(docxFile);
Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(output_path+output_file_name));
int len = -1;
byte[] buffer = new byte[1024];
//------------------追加新圖片------------------
// List<String> picList = new ArrayList<>();
Map<String,String> picFiles = (Map<String, String>) dataMap.get("picFiles");
if(picFiles!=null && !picFiles.isEmpty()){
for(String fileName :picFiles.keySet()){
ZipEntry next = new ZipEntry("word"+File.separator+"media"+File.separator+fileName);
zipout.putNextEntry(new ZipEntry(next.toString()));
InputStream in = new FileInputStream(picFiles.get(fileName));
while ((len = in.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
in.close();
}
}
//------------------追加新圖片------------------
len = -1;
while (zipEntrys.hasMoreElements()) {
ZipEntry next = zipEntrys.nextElement();
InputStream is = zipFile.getInputStream(next);
// 把輸入流的文件傳到輸出流中 如果是word/document.xml由我們輸入
zipout.putNextEntry(new ZipEntry(next.toString()));
System.out.println(">>>>>>>>>"+next.isDirectory());
System.out.println(">>>>>>>>>>>>>>>>>"+next.toString());
System.out.println(">>>>>>>>>>>>>>>>>"+next.isDirectory());
if ("word/document.xml".equals(next.toString())) {
InputStream in = new FileInputStream(xmlFile);
while ((len = in.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
in.close();
} else if(next.toString().indexOf("document.xml.rels")>0){
InputStream in = new FileInputStream(xmlConfigFile);
while ((len = in.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
in.close();
}else{
while ((len = is.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
is.close();
}
}
zipout.close();
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getFreemarkerContent(Map dataMap, String templateName,String temp_path){
String ret_str = "";
try {
//創建配置實例
Configuration configuration = new Configuration();
//設置編碼
configuration.setDefaultEncoding("UTF-8");
//ftl模板文件統一放至 com.lun.template 包下面
// configuration.setDirectoryForTemplateLoading(new File("D:/idea_workspace/alarm/alarm/src/main/resources/template/"));
configuration.setClassForTemplateLoading(FreemarkerWordUtils.class,temp_path);
//獲取模板
Template template = configuration.getTemplate(templateName);
//輸出文件
//File outFile = new File(filePath);
//如果輸出目標文件夾不存在,則創建
// if (!outFile.getParentFile().exists()){
// outFile.getParentFile().mkdirs();
// }
//將模板和數據模型合併生成文件
// Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile),"UTF-8"));
StringWriter swriter = new StringWriter();
//生成文件
template.process(dataMap, swriter);
ret_str = swriter.toString();
} catch (Exception e) {
e.printStackTrace();
}
return ret_str;
}
/**
* 刪除文件
* @param listFiles
*/
public static void delFiles(List<String> listFiles){
if(listFiles!=null && !listFiles.isEmpty()){
for(String file_temp_path:listFiles){
File file_temp = new File(file_temp_path);
if(file_temp.exists()){
file_temp.delete();
}
}
}
}
- 輸出具體的docx文檔。
項目地址:https://gitee.com/wahnn/JavaUtilsProject
主工具類是:
https://gitee.com/wahnn/JavaUtilsProject/blob/master/src/main/java/com/sl/utils/office/word/WordUtils.java