word 文檔導出 (freemaker+jacob)--java開發

工做中終於遇到了 須要導出word文旦的需求了。因爲之前沒有操做過,因此就先百度下了,基本上是:博客園,簡書,CDSN,這幾大機構的相關帖子比較多,而後花了2周時間 才初步弄懂。html

 學習順序:

第一階段java

1,。首先 是 先了解 java 經過什麼方式 來操做word的導出工做。就有了下面這個帖子了:編程

  java 操做 word 的方法 :https://www.cnblogs.com/lcngu/p/5247179.html  。新手能夠先看看了解下。ide

2. 根據需求:操做word很複雜: 1.有圖片(圖片數量 動態變化,可能沒有),2.有複選框須要展現 【一個文檔裏有好幾個 複選框。框框打鉤仍是不打勾,不肯定】,3.文檔樣式和模板客戶已經給咱們提供好了。工具

所以; 選着了使用freemaker輔助工具來操做,word文檔生成和導出。學習

 整個流程簡單來講:編碼

        對於支持低版本的ofifce  word 文檔格式 應該是 doc格式的:

                1.先生成模板:就是word文檔 先標記好要填充數據的位置,(通常建議用 實體類的屬性名 站位 ,能夠的話用花括號括起來)。小技巧(對於圖片,複選框。這些特別的東西。先不作處理)。spa

                2. 生成 xml文件:word文檔 另存爲 xml 格式的--(必定要是word.xml,其餘格式的不行,這裏選着 word-2003.xml)。低版本要求.net

                3. 生成ftl文件: 這個沒啥說的,freemaker只能能識別它,所以須要這幾個步驟1. 把生成好的xml文件,把後綴名改爲 ftl 就能夠了;2.全部佔位符的地方 加上$標誌;插件

                4. 使用freemaker 工具 整合 模板和數據了。

                5. 輸出生成好的 word文檔。

         參考教程: java使用freemarker生成word文檔步驟  帶圖片   

                             Java之利用FreeMarker導出Word實例      

         對於高版本的ofifce  word 文檔格式 是 docx格式的:

              有兩種方法:

           1.解壓--替換--壓縮。好處word是原生的不是xml格式的。

           2. 和上述doc方式同樣,只不不過導出xml 選擇 word.xml。 後續操做如出一轍。

   參考教程 : 

                          第一種方式: JAVA經過模板生成DOCX文檔    通常不推薦,除非圖片數量固定。

                       第二種方式:1.使用freemarker導出word並動態插入多張圖片。

                                            2.使用freemarker導出word文檔包含多張圖片。

 

對於複選框(打鉤和不打勾)的操做:請參考這個文檔,很是直觀的告訴你怎麼操做:freemarker導出word文檔中的複選框打鉤功能

對於圖片操做請參考 上面給的教程。 上面的教程主要是圖片的處理。

第一階段到此結束了,而後開始寫代碼和編程了。下面給的是數據獲取的幾個操做方法。數據對象Map<string ,object> .

WordPhotoUtil: 對圖片的處理

  1 package com.chiyun.peersocialworker.synchroWork.util;
  2 
  3 import com.chiyun.peersocialworker.files.entity.EmplPictureInforEntity;
  4 import com.chiyun.peersocialworker.synchroWork.vo.Picther2Vo;
  5 import com.chiyun.peersocialworker.synchroWork.vo.PictherVo;
  6 import sun.misc.BASE64Encoder;
  7 
  8 import java.io.File;
  9 import java.io.FileInputStream;
 10 import java.io.IOException;
 11 import java.io.InputStream;
 12 import java.util.ArrayList;
 13 import java.util.HashMap;
 14 import java.util.List;
 15 import java.util.Map;
 16 
 17 /**
 18  *  word-圖片信息處理-工具類
 19  * 
 20  */
 21 public class WordPhotoUtil {
 22 
 23 
 24 
 25     /**
 26      * 得到圖片的base64碼
 27      * @param filepash
 28      * @return
 29      * @throws Exception
 30      */
 31     public static String getImageBase(String filepash) throws Exception {
 32         if (filepash == null || filepash == "") {
 33             return "";
 34         }
 35         System.out.println("圖片路徑"+filepash);
 36         File file = new File(filepash);
 37         if (!file.exists()) {
 38             System.out.println("圖片不存在");
 39             return "";
 40         }
 41         InputStream in = null;
 42         byte[] data = null;
 43         try {
 44             in = new FileInputStream(file);
 45             data = new byte[in.available()];
 46             in.read(data);
 47             in.close();
 48         }catch(IOException e){
 49             e.printStackTrace();
 50         }
 51         System.out.println("圖片大小:"+data.length);
 52         BASE64Encoder encoder = new BASE64Encoder();
 53         return encoder.encode(data);
 54     }
 55 
 56 
 57 
 58 
 59     /**
 60      * 圖片信息獲取 【確定是偶數個數】 雙圖片成對保存
 61      * @param images
 62      * @return
 63      * @throws Exception
 64      */
 65     public static List<PictherVo> getImage(List<EmplPictureInforEntity> images ,int numbers,int photosize )throws Exception{
 66         List<PictherVo> pictvoList=new ArrayList<>();
 67         List<EmplPictureInforEntity> List1=new ArrayList<>();
 68         List<EmplPictureInforEntity> List2=new ArrayList<>();
 69         int i=1;
 70         for(EmplPictureInforEntity image:images ) {
 71             if( i%2==0){
 72                 List2.add(image);
 73             }else{
 74                 List1.add(image);
 75             }
 76             i++;
 77         }
 78            int bb=numbers;
 79         for(int j=0;j<(images.size()/2);j++){
 80             PictherVo pvo =new PictherVo();
 81             pvo.setTpnr1(getImageBase(List1.get(j).getTpzslj()));
 82             pvo.setTphz1(List1.get(j).getTpmc());
 83             pvo.setRidnumber1(String.valueOf(bb));
 84             pvo.setTpgs1(List1.get(j).getTpgslx());
 85             bb++;
 86             pvo.setTpnr2(getImageBase(List2.get(j).getTpzslj()));
 87             pvo.setTphz2(List2.get(j).getTpmc());
 88             pvo.setRidnumber2(String.valueOf(bb));
 89             pvo.setTpgs2(List2.get(j).getTpgslx());
 90             pictvoList.add(pvo);
 91             bb++;
 92         }
 93         return pictvoList;
 94     }
 95 
 96 
 97     /**
 98      * 圖片信息獲取 【不必定是偶數個數】 單圖片保存
 99      * @param images
100      * @return numbers
101      * @throws Exception
102      */
103     public static  List<Picther2Vo>  getImage2(List<EmplPictureInforEntity> images,int numbers,int photosize )throws Exception{
104         List<Picther2Vo> pictvoList=new ArrayList<>();
105              int RidNumber=numbers;
106         for(EmplPictureInforEntity imag:images){
107             Picther2Vo pvo =new Picther2Vo();
108             pvo.setTpnr(getImageBase(imag.getTpzslj()));
109             pvo.setTphz(imag.getTpmc());
110             pvo.setTpgs(imag.getTpgslx());
111             pvo.setRidnumber(String.valueOf(RidNumber));
112             pictvoList.add(pvo);
113             RidNumber++;
114         }
115 
116 
117         return pictvoList;
118     }
119 
120     /**
121      *  輸出圖片信息集合-成雙圖片信息
122      * @param images 圖片內容
123      * @param numbers Rid 起始編號
124      * @return
125      * @throws Exception
126      */
127    public static  List<Map<String, Object>> getphotoInfo(  List<EmplPictureInforEntity>  images ,int numbers,int photosize )throws Exception{
128          System.out.println(images.size());
129        List<Map<String, Object>> info = new ArrayList<>();
130        List<PictherVo> picvoList=null;
131        if(images.size()==0){
132            return info;
133        }else {//必須有圖片
134            String str = images.get(0).getTpzslj();
135            String defoTpPath = str.substring(0, str.indexOf("upload\\"));
136            if ( images.size()% 2 == 0) { //偶數張
137                picvoList =getImage(images,numbers,photosize);
138 
139            } else { //奇數張
140                EmplPictureInforEntity defoimage = new EmplPictureInforEntity();
141                defoimage.setTpmc("back.png");
142                defoimage.setTpzslj(defoTpPath + "upload\\" + "back.png");
143                defoimage.setTpgslx("png");
144                images.add(defoimage);
145                picvoList = getImage(images,numbers,photosize);
146 
147            }
148            for (PictherVo vo : picvoList) {
149                Map<String, Object> map = new HashMap<>();
150                map.put("photo1", vo.getTpnr1());
151                map.put("mc1", vo.getTphz1());
152                map.put("hz1",vo.getTpgs1());
153                map.put("rId1",vo.getRidnumber1());
154                map.put("photo2", vo.getTpnr2());
155                map.put("mc2", vo.getTphz2());
156                map.put("hz2",vo.getTpgs2());
157                map.put("rId2",vo.getRidnumber2());
158                info.add(map);
159            }
160        }
161 
162         return info;
163    }
164 
165     /**
166      * 輸出圖片信息集合-單張圖片信息
167      * @param images 圖片內容
168      * @param numbers Rid 起始編號
169      * @param photosize 圖片總數
170      * @return
171      * @throws Exception
172      */
173     public static  List<Map<String, Object>> getphotoInfo2(  List<EmplPictureInforEntity>  images ,int numbers,int photosize )throws Exception{
174         List<Map<String, Object>> info = new ArrayList<>();
175         List<Picther2Vo> picvoList=null;
176 
177         if(images.size()==0){
178             return info;
179         }else {
180             picvoList = getImage2(images,numbers,photosize);
181         }
182         for(Picther2Vo vo: picvoList){
183             Map<String, Object> map = new HashMap<>();
184             map.put("photo",vo.getTpnr());
185             map.put("tpmc",vo.getTphz());
186             map.put("hz",vo.getTpgs());
187             map.put("rId",vo.getRidnumber());
188             info.add(map);
189         }
190         return info;
191     }
192 
193 
194 }
View Code

getObjectToMap():  把 實體類轉換成map

    //對象轉Map-主表信息排除時間格式的
    private static  Map<String, Object> getObjectToMap2(Object object)throws Exception{
        Map<String, Object> map = new HashMap<>();
        for (Field f : object.getClass().getDeclaredFields()) {
            f.setAccessible(true);
            if(f.getType().toString().equals("class java.lang.String")){
                map.put(f.getName(),StringUtil.getNullorString((String)f.get(object)));
            }else if(f.getType().toString().equals("class java.util.Date")){

            }else if(f.getType().toString().equals("class java.lang.Integer")){
                map.put(f.getName(),StringUtil.getNullorString(((String)f.get(object))));
            }else{
                System.out.println("這是新的屬性:"+f.getType().toString());
                map.put(f.getName(),StringUtil.getNullorString(((String)f.get(object))));
            }

        }
        return map;
    }
View Code

WordCheckboxUtil: 操做複選框

package com.chiyun.peersocialworker.synchroWork.util;

import com.chiyun.peersocialworker.utils.StringUtil;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * word-複選框操做-工具類
 *
 */
public class WordCheckboxUtil {

    /**
     * 單選-複選框信息處理
     * @param dictdm 打鉤值
     * @param bjname  標記名稱-模板佔位符
     * @param list  全部詞條信息
     * @return
     */
    public static Map<String, Object> getCheckbox1(String dictdm,String bjname,List<DictionarylistEntity> list){
        Map<String, Object> dataMap = new HashMap<>();
        int index =0;
        for(DictionarylistEntity entity:list){
            index=index+1;
            if(entity.getCtdm().equals(dictdm)){
                dataMap.put(bjname+index,true);

            }else {
                dataMap.put(bjname+index,false);

            }
        }
        return  dataMap;
    }


    /**
     *
     * 多選-複選框信息處理
     * @param dictdm 打鉤值
     * @param bjname  標記名稱-模板佔位符
     * @param list  全部詞條信息
     * @return
     */
    public static Map<String, Object> getCheckbox2(String dictdm,String bjname,List<DictionarylistEntity> list){
        String[] zsxqdms=null;
        if(StringUtil.isNull(dictdm)){

        }else{
         zsxqdms=dictdm.split(","); //用英文逗號分隔

        }
        Map<String, Object> dataMap = new HashMap<>();
        int index =0;
        for(DictionarylistEntity entity:list){
            index=index+1;
            boolean flag=false;
            if(zsxqdms!=null){
             for(String str1 :zsxqdms){
                 if(entity.getCtdm().equals(str1)) {
                    flag=true;
                 }
             }
            }else {

            }
            dataMap.put(bjname+index,flag);

        }
        return  dataMap;
    }

    public static void main(String[] args) {
        String dictdm="123,03,45";
        String[] zsxqdms=dictdm.split(","); //用英文逗號分隔
        System.out.println(zsxqdms.length);
        System.out.println(zsxqdms[0]);
    }

}
View Code

word 工具類:WordUtil

package com.chiyun.peersocialworker.utils;

import freemarker.template.Configuration;
import freemarker.template.Template;

import java.io.*;
import java.util.Map;

/**
 * 
 */
public class WordUtil {

    /**
     * 生成word文件
     * @param dataMap word中須要展現的動態數據,用map集合來保存
     * @param templateName word模板名稱,例如:test.ftl
     * @param filePath 文件生成的目標路徑,例如:D:/wordFile/
     * @param fileName 生成的文件名稱,例如:test.doc
     */
    @SuppressWarnings("unchecked")
    public static void createWord(Map dataMap,String templateName,String filePath,String fileName){
        try {
            //建立配置實例
            Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);

            //設置編碼
            configuration.setDefaultEncoding("UTF-8");

            //ftl模板文件
            configuration.setClassForTemplateLoading(WordUtil.class,"/template/new");

            //獲取模板
            Template template = configuration.getTemplate(templateName);

            //輸出文件
            File outFile = new File(filePath+File.separator+fileName);

            //若是輸出目標文件夾不存在,則建立
            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();
        }
    }

    /**
     * 生成word文件
     * @param dataMap word中須要展現的動態數據,用map集合來保存
     * @param templateName word模板名稱,例如:test.ftl
     * @param filePath 文件生成的目標路徑,例如:D:/wordFile/
     * @param fileName 生成的文件名稱,例如:test.doc
     */
    @SuppressWarnings("unchecked")
    public static void createWord2(Map dataMap,String templateName,String filePath,String fileName){
        try {
            //建立配置實例
            Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);

            //設置編碼
            configuration.setDefaultEncoding("UTF-8");

            //ftl模板文件
            configuration.setClassForTemplateLoading(WordUtil.class,"/template/new");

            //獲取模板
            Template template = configuration.getTemplate(templateName);

            //輸出文件
            File outFile = new File(filePath+File.separator+fileName);

            //若是輸出目標文件夾不存在,則建立
            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();
        }
    }
}
View Code

 

總結: 

    固然上面的幾個方法 確定不是所有 ,只是其中對數據處理的幾個手段而已;值得注意的是:freemaker 操做,不能返回 null ,已返回就報錯。須要轉化成 「 」才行

這裏遇到的問題:

能夠參考一下帖子:

         java使用freemarker動態生成world文檔及常見錯誤解決

     經過freemarker生成一個word 解決生成的word用wps打開有問題的問題,解決出word時中文文件名亂碼問題

上面給的建議,基本上是屬於導出前屬於執著模板的時候纔會出現。

第二階段: 要兼容 office和 WPS    

    1.需求變動: 客戶 使用羣體,有的是office,有的是WPS;PC端。能夠不用支持低版本的office (由於他們最低都是用的是office2010版本的)

    如今就遇到了問題:  用office 打開可以正常顯示  ,而用 WPS打開 ,能容都能出來,就是格式不對,【在office 裏方框 和文字不會重疊,而WPS 卻會重疊】。這是兼容性問題,仍是WPS和office 這個樣式的標號不同。

那麼久只能另想其餘方法了。

   首先分析: 我使用模板格式是: 第一種方式兼容2003版本的也就是  導出xml是 2003word.xml。  

    而後就會發現 office 有這個格式,而WPS沒有,只有一個 word.xml 格式的。 而後百度了下,才知道 word.xml是高版本的,也就是說WPS 不支持低版本的了。

而後嘗試完善:

導出文檔 名稱的後綴改爲 docx.。而後神奇的發現 ,導出的文檔,office 和WPS 都不能打開了。 這個方式失敗,放棄。

    第二種方式:

        那我就導 docx 格式的吧,使用docx 第一種方式 。我直接放棄了,就是由於那個該死的圖片數量不定,以及生成的文檔類型多個,並且還要重寫調用方式 太費時間了。

就採用第二種方式了,整個過程和doc 同樣,只是模板裏的排版有些變化,調用方式基本上不變。

     當採用 docx的第二種方式時,你會發現,生成的xml文件 幾乎如出一轍,惟一的區別就是 圖片了;word-2003.xml  圖片的配置信息就在一個地方。,而word.xml 圖片分散開來了,在三個標籤裏了,一個圖片展現位置,一個圖片對應映射關係,一個圖片內容存放位置。這個也簡單,由原來的一個list集合改爲 3個list集合 分別處理就好了。(具體操做請看上面提供的帖子)

   一番操做後。

導出文件 ,文件後綴 寫的是 docx ;而後打開 才發現:office 打不開,可是wps可以正常打開,並且顯示正常。

 最後卡在這裏大概3天 ,網上各類找資料; 結果在無心間 解決了。

    這個問題的解決辦法:

 解決方法 是: 導出文件的後綴名改爲 doc 。這樣就好了,導出的文檔是doc格式的。不管是office仍是WPS都能正常打開了。萬事大吉了

 這就然咱們充分認識到了,若是使用的電腦是XP系統的,那麼對不起,就不要用WPS了; 若是是高版本的系統,請使用2007版級以上的office吧。這樣就不怕兼容性的問題了;

  同時也說明了一個問題:模板原文是doc仍是docx 都不是問題,只要另存爲 word,xml就是同樣的了。 生成的文件 格式 是doc仍是docx ,取決於你給文件定的後綴。 

第三階段: 要兼容移動端了   

       後面因爲客戶以爲咱們的系統不錯,就叫咱們順便也作個移動端吧,移動端吧。(差價的錢已到位)。  而後拿手機打開生成的word文檔。

靠,竟然打不開,顯示的東西和咱們生成的xml模板如出一轍的調調。

      也就是說咱們用freemaker 生成的word文檔 (以到xml模板的方式)其實內容是xml格式的,不是標準的word了。【也就是: 生成的模板xml文件 只是從命名一下(把後綴名改爲doc或者docx)】?

因此,移動端固然就打不開了啊。

    回去試了下,把 xml模板 後綴改爲 doc或者docx ,用office和WPS打開,結果也能正常打開和顯示; 

    我有繼續試了下這個,把系統生成好的word文檔打開另存爲 docx或者doc .而後把另存好的文件發到手機上,打開下,結果就能正常顯示了。說明,咱們的word文檔就差一個把很是規word轉化成正規的word文檔操做了。(也就是像 office 軟件那個另存爲 的強大功能)。

在這裏深深的譴責下那些  :提出相同問題,並解決了的,但就是不告訴你怎麼解決的坑爹樓主們了。害的各位同胞在線急等。

  解決方法: 使用jacob 插件,把你生成好的word文檔轉成 正規的wordw文檔。

 參考文檔:

              1.  Java 將xml模板動態填充數據轉換爲word文檔

             2. jacob 官網 下載 相關jar包和dll文件。  

             3.jacob 安裝手冊 1     安裝配置2

相關文章
相關標籤/搜索