java利用Freemarker模板生成格式友好的doc或者docx文檔

之前寫過一篇利用Freemarker模板生成doc的博客,不過那個博客有點缺陷,不支持生成docx格式的文檔。所以,這裏補充一篇,生成docx或doc格式的文檔以具體的docx模板或者doc模板爲主。這裏以docx爲例。
具體思路:

  1. 把docx文檔修改爲ZIP格式(修改.docx後綴名爲.zip)
  2. 獲取zip裏的document.xml文檔以及_rels文件夾下的document.xml.rels文檔
  3. 把內容填充到document.xml裏,以及圖片配置信息填充至document.xml.rels文檔裏
  4. 在輸入docx文檔的時候把填充過內容的的 document.xml、document.xml.rels用流的方式寫入zip(詳見下面代碼)。
  5. 把圖片寫入zip文件下word/media文件夾中
  6. 輸出docx文檔

docx模板修改成zip格式後的信息如下(因爲word文檔本身就是ZIP格式實現的)
這裏寫圖片描述
這裏寫圖片描述

  • document.xml裏存放主要數據
  • media存放圖片信息
  • _rels裏存放配置信息

注意:如果docx模板裏的圖片帶有具體路徑的話,則圖片的格式不受限制。
這裏寫圖片描述
如果docx模板裏裏圖片信息不帶路徑,則模板僅支持和模板圖片類型一致的圖片。
這裏寫圖片描述


交代了這麼多,下面就可以乾貨。

  1. 準備好docx模板
    這裏寫圖片描述
    這裏寫圖片描述

  2. 把docx文檔修改爲ZIP格式(修改.docx後綴名爲.zip)

  3. 獲取zip文件裏的word文件夾下的document.xml以及_rels文件夾裏的document.xml.rels文件作爲模板。
    這裏寫圖片描述
    這裏寫圖片描述
    這裏寫圖片描述
    注意:這裏圖片配置信息是根據 rId來獲取的。docx模板總的${img_warn}就是rId的具體值。
    爲了避免重複,我的圖片rId從17開始(在我沒有修改之前,裏面最大的rId是rId17)。

  4. 填充模板信息、寫入圖片信息。

        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();
                }
            }
        }
    }
  1. 輸出具體的docx文檔。
    這裏寫圖片描述
    這裏寫圖片描述

項目地址:https://gitee.com/wahnn/JavaUtilsProject
主工具類是:
https://gitee.com/wahnn/JavaUtilsProject/blob/master/src/main/java/com/sl/utils/office/word/WordUtils.java

發佈了82 篇原創文章 · 獲贊 36 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章