1. 程式人生 > >使用freemarker匯出html格式的word(續)

使用freemarker匯出html格式的word(續)

本文接上文(https://blog.csdn.net/u011099093/article/details/81010298)繼續探討匯出word中攜帶圖片問題。

在參考了博文http://www.cnblogs.com/liaofeifight/p/5484891.html後,改用.mht格式的模板進行編輯,首先介紹下整體思路:

1.準備好一個word(.doc格式),在裡面編輯好自己想要的內容,把需要動態替換的部分 以引數替代,另存為.mht單網頁;

2.對.mht單網頁內容進行微調(該檔案每行應該有長度限制,部分換行時會以=結束),作為第一個模板,程式替換引數後返回body區域的html文字,放入富文字編輯器進行編輯及插入圖片;

3.將編輯器內容傳至後臺解析,分解為指定的幾個部分,freemarker替換第二個.mht模板中的引數,匯出為word;

 

一、準備mht模板

這裡需要準備兩個模板:

第一個模板是替換詳細資料用的:根據需要編輯一個word,將需要替換的部分改為指定引數:

然後將word另存為但網頁檔案(mht),對mht檔案進行微調,由於該檔案會自動換行,同時在行末新增一個=連線符,所以主要將第一個<body></body>標籤中不必要的=刪掉,以便後面提取到編輯器中時樣式不會亂。

這裡要注意複製模板內容到專案ftl檔案中時部分行首會有空格,需要將所有行頂格,這是.ftl檔案<body>部分的程式碼,中文都被自動轉換為ascii十進位制編碼:

<body>

<div class=3DWordSection1 style=3D'layout-grid:21.75pt'>

<p class=3DMsoTitle style=3D'margin-right:16.0pt;text-indent:0cm;
   mso-char-i=ndent-count:
0'><span style=3D'mso-bidi-font-size:22.0pt;font-family:SimSun;
         mso-ascii-font-family:
Cambria;mso-ascii-theme-font:major-latin;mso-hansi-font-family:Cambria;
mso-hansi-theme-font:major-latin'>&#27979;&#35797;&#25253;&#21578;</span>
    <span
lang=3DEN-US style=3D'mso-bidi-font-size:22.0pt'><o:p></o:p></span></p>

<h1 style=3D'margin-left:21.0pt;text-indent:-21.0pt;mso-char-indent-count:0;
mso-list:l0 level1 lfo2'><![if !supportLists]><span lang=3DEN-US
style=3D'mso-bidi-font-family:SimSun;mso-bidi-theme-font:major-fareast'>
        <span
style=3D'mso-list:Ignore'>&#19968;&#12289;<span
                    style=3D'font:7.0pt "Times New Roman"'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</span></span></span><![endif]>
    <span style=3D'font-family:SimSun;mso-ascii-font-family:
Calibri;mso-ascii-theme-font:minor-latin;mso-fareast-font-family:SimSun;
mso-fareast-theme-font:major-fareast;mso-hansi-font-family:Calibri;
          mso-hansi-theme-font:
minor-latin'>&#27010;&#35201;</span></h1>

<p class=3DMsoNormal style=3D'margin-right:15.85pt;
   mso-para-margin-right:.99gd;
text-indent:32.0pt'><span style=3D'mso-bidi-font-size:16.0pt;
  font-family:\4EFF\5B8B'>&#23545;&#20110;&#23548;&#20986;<span
lang=3DEN-US>Word
</span>&#24182;&#25658;&#24102;&#22270;&#29255;&#30340;<span
lang=3DEN-US>123</span>
&#31181;&#26041;&#27861;&#25506;&#32034;&#65292;&#26597;
    &#35810;&#21040;&#20351;&#29992;<span
lang=3DEN-US>itext</span>&#36716;&#25442;&#20449;&#24687;<span
lang=3DEN-US>${num1!}</span>&#26465;&#65292;&#20351;&#29992;<span
lang=3DEN-US>openoffice</span>&#36716;&#25442;&#20449;&#24687;<span
lang=3DEN-US>${num2!}</span>&#26465;&#65292;<span
lang=3DEN-US>pageoffice</span>&#36716;&#25442;&#20449;&#24687;<span
lang=3DEN-US>${num3!}</span>&#26465;&#65292;<span
lang=3DEN-US>poi</span>&#36716;&#25442;&#20449;&#24687;<span
    lang=3DEN-US>${num4!}</span>&#26465;&#65292;<span
lang=3DEN-US>freemarker</span>&#36716;&#25442;&#20449;&#24687;<span
    lang=3DEN-US>${num5!}</span>&#26465;&#12290;<span
lang=3DEN-US><o:p></o:p></span></span></p>

<h2 style=3D'margin-left:21.0pt;text-indent:-21.0pt;mso-char-indent-count:0;
mso-list:l1 level1 lfo4'><![if !supportLists]><span lang=3DEN-US
style=3D'font-family:SimSun;mso-ascii-theme-font:major-fareast;
    mso-fareast-font-family:
SimSun;mso-fareast-theme-font:major-fareast;
    mso-hansi-theme-font:major-fareast;
mso-bidi-font-family:SimSun;mso-bidi-theme-font:major-fareast'><span
style=3D'mso-list:Ignore'>(&#19968;)<span
    style=3D'font:7.0pt "Times New Roman"'>&nbsp;&nbsp;
</span></span></span><![endif]>
    <span style=3D'font-family:SimSun;mso-ascii-font-family:
Cambria;mso-ascii-theme-font:major-latin;mso-fareast-font-family:SimSun;
mso-fareast-theme-font:major-fareast;mso-hansi-font-family:Cambria;
  mso-hansi-theme-font:
major-latin'>&#34920;&#26684;&#23637;&#31034;</span></h2>

<table class=3DMsoTableGrid border=3D1 cellspacing=3D0
       cellpadding=3D0 align=3Dleft
width=3D1093 style=3D'width:546.3pt;border-collapse:collapse;border:none;
mso-border-alt:solid windowtext .5pt;mso-table-overlap:never;
       mso-yfti-tbllook:
1184;mso-table-lspace:9.0pt;margin-left:7.5pt;mso-table-rspace:9.0pt;
margin-right:7.5pt;mso-table-anchor-vertical:paragraph;
       mso-table-anchor-horizontal:
column;mso-table-left:left;mso-table-top:.05pt;
       mso-padding-alt:0cm 5.4pt 0cm 5.4pt'>
<tr style=3D'mso-yfti-irow:0;mso-yfti-firstrow:yes;height:29.2pt'>
<td width=3D182 style=3D'width:91.2pt;border:solid windowtext 1.0pt;
    mso-border-alt:
solid windowtext .5pt;padding:0cm 5.4pt 0cm 5.4pt;height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'>
    <b style=3D'mso-bidi-font-weight:
normal'><span lang=3DEN-US style=3D'font-size:12.0pt'>itext<o:p></o:p>
    </span></b></p>
</td>
<td width=3D182 style=3D'width:91.2pt;border:solid windowtext 1.0pt;
    border-left:
none;mso-border-left-alt:solid windowtext .5pt;
    mso-border-alt:solid windowtext .5pt;
padding:0cm 5.4pt 0cm 5.4pt;height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter
   style=3D'text-align:center;text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'>
    <b style=3D'mso-bidi-font-weight:
normal'><span lang=3DEN-US
  style=3D'font-size:12.0pt'>openOffice<o:p></o:p></span></b></p>
</td>
<td width=3D183 style=3D'width:91.25pt;
    border:solid windowtext 1.0pt;border-left:
none;mso-border-left-alt:solid windowtext .5pt;
    mso-border-alt:solid windowtext .5pt;
padding:0cm 5.4pt 0cm 5.4pt;height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'>
    <b style=3D'mso-bidi-font-weight:
normal'><span lang=3DEN-US style=3D'font-size:12.0pt'>PageOffice
        <o:p></o:p></span></b></p>
</td>
<td width=3D183 style=3D'width:91.25pt;border:solid windowtext 1.0pt;
    border-left:
none;mso-border-left-alt:solid windowtext .5pt;
    mso-border-alt:solid windowtext .5pt;
padding:0cm 5.4pt 0cm 5.4pt;height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'>
    <b style=3D'mso-bidi-font-weight:
normal'><span lang=3DEN-US style=3D'font-size:12.0pt'>Poi<o:p></o:p>
    </span></b></p>
</td>
<td width=3D183 style=3D'width:91.25pt;border:solid windowtext 1.0pt;
    border-left:
none;mso-border-left-alt:solid windowtext .5pt;
    mso-border-alt:solid windowtext .5pt;
padding:0cm 5.4pt 0cm 5.4pt;height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'>
    <b style=3D'mso-bidi-font-weight:
normal'><span lang=3DEN-US style=3D'font-size:12.0pt'>Freemarker
        <o:p></o:p></span></b></p>
</td>
<td width=3D183
style=3D'width:91.25pt;border:solid windowtext 1.0pt;border-left:
none;mso-border-left-alt:solid windowtext .5pt;
    mso-border-alt:solid windowtext .5pt;
padding:0cm 5.4pt 0cm 5.4pt;height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'>
    <b style=3D'mso-bidi-font-weight:
normal'><span style=3D'font-size:12.0pt;font-family:\4EFF\5B8B;
              mso-ascii-font-family:
Calibri;mso-ascii-theme-font:minor-latin;mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin'>&#32479;&#35745;&#20154;</span></b><b
style=3D'mso-bidi-font-weight:normal'>
<span lang=3DEN-US style=3D'font-size:12.0pt'><o:p></o:p></span></b></p>
</td>
</tr>
<#if listOfData??>
    <#list listOfData as listData>
<tr style=3D'mso-yfti-irow:1;mso-yfti-lastrow:yes;height:29.2pt'>
<td width=3D182 style=3D'width:91.2pt;border:solid windowtext 1.0pt;
    border-top:
none;mso-border-top-alt:solid windowtext .5pt;
    mso-border-alt:solid windowtext .5pt;
padding:0cm 5.4pt 0cm 5.4pt;height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'><span lang=3DEN-US
style=3D'font-size:12.0pt;font-family:\4EFF\5B8B'>${listData.sfzh!}<o:p></o:p>
    </span></p>
</td>
<td width=3D182 style=3D'width:91.2pt;border-top:none;border-left:none;
border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
mso-border-top-alt:solid windowtext .5pt;
    mso-border-left-alt:solid windowtext .5pt;
mso-border-alt:solid windowtext .5pt;padding:0cm 5.4pt 0cm 5.4pt;
    height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'><span lang=3DEN-US
style=3D'font-size:12.0pt;font-family:\4EFF\5B8B'>${listData.xm!}<o:p></o:p></span>
    </p>
</td>
<td width=3D183 style=3D'width:91.25pt;border-top:none;border-left:none;
border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
mso-border-top-alt:solid windowtext .5pt;
    mso-border-left-alt:solid windowtext .5pt;
mso-border-alt:solid windowtext .5pt;padding:0cm 5.4pt 0cm 5.4pt;
    height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'><span lang=3DEN-US
style=3D'font-size:12.0pt;font-family:\4EFF\5B8B'>${listData.czje!}<o:p></o:p>
    </span></p>
</td>
<td width=3D183 style=3D'width:91.25pt;border-top:none;border-left:none;
border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
mso-border-top-alt:solid windowtext .5pt;
    mso-border-left-alt:solid windowtext .5pt;
mso-border-alt:solid windowtext .5pt;padding:0cm 5.4pt 0cm 5.4pt;
    height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'><span lang=3DEN-US
style=3D'font-size:12.0pt;font-family:\4EFF\5B8B'>${listData.czcs!}<o:p></o:p></span>
    </p>
</td>
<td width=3D183 style=3D'width:91.25pt;border-top:none;border-left:none;
border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
mso-border-top-alt:solid windowtext .5pt;
    mso-border-left-alt:solid windowtext .5pt;
mso-border-alt:solid windowtext .5pt;padding:0cm 5.4pt 0cm 5.4pt;
    height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'><span lang=3DEN-US
style=3D'font-size:12.0pt;font-family:\4EFF\5B8B'>${listData.rzje!}<o:p></o:p>
    </span></p>
</td>
<td width=3D183 style=3D'width:91.25pt;border-top:none;border-left:none;
border-bottom:solid windowtext 1.0pt;border-right:solid windowtext 1.0pt;
mso-border-top-alt:solid windowtext .5pt;
    mso-border-left-alt:solid windowtext .5pt;
mso-border-alt:solid windowtext .5pt;padding:0cm 5.4pt 0cm 5.4pt;
    height:29.2pt'>
<p class=3DMsoNormal align=3Dcenter style=3D'text-align:center;
   text-indent:0cm;
mso-char-indent-count:0;mso-element:frame;mso-element-frame-hspace:9.0pt;
mso-element-wrap:around;mso-element-anchor-vertical:paragraph;
   mso-element-anchor-horizontal:
column;mso-element-top:.05pt;mso-height-rule:exactly'>
    <span style=3D'font-size:
12.0pt;font-family:\4EFF\5B8B'>${listData.rzcs!}<span
lang=3DEN-US><o:p></o:p></span></span></p>
</td>
</tr>
    </#list>
</#if>
</table>

<p class=3DMsoNormal style=3D'text-indent:0cm;mso-char-indent-count:0'><span
lang=3DEN-US style=3D'mso-bidi-font-size:16.0pt;font-family:\4EFF\5B8B'>
        <o:p>&nbsp;</o:p></span></p>

<p class=3DMsoNormal style=3D'text-indent:0cm;mso-char-indent-count:0'><span
style=3D'mso-bidi-font-size:16.0pt;font-family:\4EFF\5B8B'>
&#21704;&#21704;&#21704;&#65292;&#25104;&#21151;&#20102;&#65281;<span
lang=3DEN-US><o:p></o:p></span></span></p>

<p class=3DMsoNormal style=3D'text-indent:0cm;mso-char-indent-count:0'><span
lang=3DEN-US style=3D'mso-bidi-font-size:16.0pt;font-family:\4EFF\5B8B'>
        <o:p>&nbsp;</o:p></span></p>

</div>

</body>

第二個模板是整體替換編輯器內容用的:

主要在第一個<body>內,圖片base64替換部分,文件末尾圖片引用處,三個部分進行替換:

二、程式替換引數,獲取替換後內容返回編輯器

/**
     * 替換模板引數,提取替換引數的文件內容,返回前臺的富文字編輯器中
     * @param request
     * @return
     */
    @ResponseBody
    @RequestMapping("getFtlContent")
    public Map getFtlContent(HttpServletRequest request) {
        HashMap result = new HashMap();
        Map<String, Object> data = new HashMap<>();
        data.put("num1", "123");
        data.put("num2", "9723947");
        data.put("num3", "6712");
        data.put("num4", "792343200");
        data.put("num5", "763");
        List listOfData = new ArrayList();
        Map<String, Object> data1 = new HashMap<>();
        Map<String, Object> data2 = new HashMap<>();
        data1.put("sfzh", "340120432232");
        data2.put("sfzh", "340120432231");
        data1.put("xm", "32423");
        data2.put("xm", "234233");
        data1.put("czje", "72823627");
        data1.put("czje", "728236700");
        data1.put("czcs", "873");
        data2.put("czcs", "673");
        data1.put("rzje", "80238409");
        data2.put("rzje", "27934239");
        data1.put("rzcs", "張三");
        data2.put("rzcs", "李四");
        listOfData.add(data1);
        listOfData.add(data2);
        data.put("listOfData", listOfData);
        try {
            Map<String, String> parameters = this.getParameters(request);

            ServletContext context = request.getServletContext();
            String rootPath = request.getRealPath("/");

            Configuration configuration = new Configuration();
            configuration.setDefaultEncoding("utf-8");
            configuration.setServletContextForTemplateLoading(context, "/freemarkTemplate");
            Template template = configuration.getTemplate("mhtTemplate.ftl", "utf-8");

            String exportPath = rootPath + "export" + File.separator;
            File file = new File(exportPath);
            if (!file.exists()) {
                file.mkdirs();
            }
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(exportPath + "test.doc"))));
            WordHtmlGeneratorHelper.handleAllObject(data);
            template.process(data, out, ObjectWrapper.BEANS_WRAPPER);

            Configuration configuration1 = new Configuration();
            configuration1.setDefaultEncoding("utf-8");
            configuration1.setServletContextForTemplateLoading(context, "/export");
            Template t = configuration1.getTemplate("test.doc", "utf-8");

            result.put("value", t.toString());
            result.put("state", "success");
        } catch (Exception e) {
            result.put("state", "error");
            e.printStackTrace();
        }
        return result;
    }

這裡有個地方需要注意,由於mht中對中文都是用ascii十進位制碼去替換的,所以在使用process()方法替換引數前需要將資料中的中文處理:

import java.lang.reflect.Field;
import java.util.*;

import com.jessica.word_mht.bean.ProjectPepole;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.FieldCallback;



/**   
* @Description:word 網頁匯出(單檔案網頁匯出,mht檔案格式)
* @author:LiaoFei  
* @date :2016-3-28 上午11:17:38  
* @version V1.0  
*   
*/
public class WordHtmlGeneratorHelper  {
	
	/**   
	* @Description: 將字元換成3Dus-asci,十進位制Accsii碼
	* @param @param source
	* @param @return    
	* @return String    
	* @throws
	* @author:LiaoFei
	* @date:2016-3-28 上午11:18:39
	*/ 
	public static String string2Ascii(String source){
		if(source==null || source==""){
			return null;
		}
		StringBuilder sb=new StringBuilder();
		
		char[] c=source.toCharArray();
		for(char item : c){
			String itemascii="";
			if(item>=19968 && item<40623){
				itemascii=itemascii="&#"+(item & 0xffff)+";";
			}else{
				itemascii=item+"";
			}
			sb.append(itemascii);
		}
		
		return sb.toString();
		
	}
	
	/**   
	* @Description: 將object的所有屬性值轉成成3Dus-asci編碼值
	* @param @param object
	* @param @return    
	* @return T    
	* @throws
	* @author:LiaoFei
	* @date:2016-3-29 下午2:56:24
	*/ 
	public static <T extends Object> T handleObject2Ascii(final T toHandleObject){
		
		class myFieldsCallBack  implements FieldCallback{

			@Override
			public void doWith(Field f) throws IllegalArgumentException,
					IllegalAccessException {
				if(f.getType().equals(String.class)){
					//如果是字串型別
					f.setAccessible(true);
					String oldValue=(String)f.get(toHandleObject);
					if(!StringUtils.isEmpty(oldValue)){
						f.set(toHandleObject, string2Ascii(oldValue));
					}
					
					//f.setAccessible(false);
				}
			}
		}
	
		ReflectionUtils.doWithFields(toHandleObject.getClass(), new myFieldsCallBack());
		
		return toHandleObject;
	}
	
	
	public static <T extends Object> List<T> handleObjectList2Ascii(final List<T> toHandleObjects){
		
		for (T t : toHandleObjects) {
			handleObject2Ascii(t);
		}
		
		return toHandleObjects;
	}
	
	
	public static void handleAllObject(Map<String, Object> dataMap){
		
		//去處理資料
    	for (Map.Entry<String, Object> entry : dataMap.entrySet()){
    		Object item=entry.getValue();
    		
    		//判斷object是否是primitive type 
    		if(isPrimitiveType(item.getClass())){
    			if(item.getClass().equals(String.class)){
    				item=WordHtmlGeneratorHelper.string2Ascii((String)item);
    				entry.setValue(item);
    			}
    		}else if(isCollection(item.getClass())){
    			for (Object itemobject : (Collection)item) {
    				if(isMap(itemobject.getClass())){
						WordHtmlGeneratorHelper.handleAllObject((Map)itemobject);
					}else{
    					WordHtmlGeneratorHelper.handleObject2Ascii(itemobject);
					}
				}
    		}else{
    			WordHtmlGeneratorHelper.handleObject2Ascii(item);
    		}
    	}
		
	}
	
	public static String joinList(List<String> list,String join ){
		StringBuilder sb=new StringBuilder();
		for (String t : list) {
			sb.append(t);
			if(!StringUtils.isEmpty(join)){
				sb.append(join);
			}
		}
		
		return sb.toString();
	} 
	
	
	private static boolean isPrimitiveType(Class<?> clazz){
    	return clazz.isEnum() ||
		CharSequence.class.isAssignableFrom(clazz) ||
		Number.class.isAssignableFrom(clazz) ||
		Date.class.isAssignableFrom(clazz);
    	
    }
    private static boolean isCollection(Class<?> clazz){
    	return Collection.class.isAssignableFrom(clazz);
    }
	private static boolean isMap(Class<?> clazz){
		return Map.class.isAssignableFrom(clazz);
	}
	
	
}

前臺獲取: 

function initReport() {
    $('#saveEdit').hide();
    $('#insertImg').hide();
    $('#exitEdit').hide();

    $('#exportWord').show();
    $('#editModel').show()
    $('#docDiv').show();
    UE.getEditor('editor').setHide();

    $.ajax({
        url:_ctx+'/report/common/getFtlContent',
        data:{},
        type:'post',
        dataType:'json',
        success:function (obj) {
            var value=obj.value;
            //只獲取第一個<body></body>中的內容
            var temp=value.split('<body>');
            var temp1=temp[1].split('</body>');
            // var content='<body lang=3DZH-CN style=3D\'tab-interval:21.0pt;\n' +
            //     'text-justify-trim:punctuation\'>'+temp1[0]+'</body>';
            //去除mht模板中的3D字元
            var content=temp1[0].replace(/=3D/g,'=');
            // console.info(content)
            $('#docDiv').empty().append(content)
        },error:function () {
            alert('error')
        }
    })
}

三、插入圖片,處理html內容,匯出word

前臺獲取編輯器內容:

function exportWordFunc(){
    var content=UE.getEditor('editor').getContent();

    $.ajax({
        url:_ctx+'/report/common/exportHtml',
        data:{content:content},
        type:'post',
        dataType:'json',
        success:function (obj) {
            var filepath=obj.filepath;
            console.info(filepath)
            if(filepath!=null){
                var encode2=encodeURIComponent(filepath);
                console.info(encode2)
                console.log(_ctx+'/report/common/downloadWord?'+(new Date().getTime())+"&filepath="+encode2)
                window.location.href=_ctx+'/report/common/downloadWord?'+(new Date().getTime())+"&filepath="+encode2;
            }else{
                alert("下載word失敗!");
            }
        }
    })
}

後臺處理:

/**
     * 處理前臺獲取的編輯器內容,匯出為word
     * @param request
     * @param content
     * @return
     */
    @ResponseBody
    @RequestMapping("exportHtml")
    public Map<String, String> exportHtml(HttpServletRequest request, String content) {
        Map<String, String> resultMap = new HashMap<>();
        try {
            //遍歷獲取所有引數
            Map<String, String> parameters = this.getParameters(request);

//            ==================================================
//            處理前臺獲取的富文字html
//            content = content.replace("=", "=3D");
            RichHtmlHandler handler = new RichHtmlHandler(content);

//            handler.setDocSrcLocationPrex("file:///C:/70ED9946");
            handler.setDocSrcLocationPrex("file:///C:/ABF5A891");
//            handler.setDocSrcParent("file9462.files");
            handler.setDocSrcParent("file3409.files");
//            handler.setNextPartId("01D189BB.30229F00");
            handler.setNextPartId("01D49DCC.3B096E30");
            handler.setShapeidPrex("_x56fe__x7247__x0020");
            handler.setSpidPrex("_x0000_i");
            handler.setTypeid("#_x0000_t75");

            handler.handledHtml(true, request);

//            獲取處理為mht內容的html
            Map<String,Object> data=new HashMap<>();
            data.put("htmlContent",handler.getHandledDocBodyBlock());
            if (handler.getDocBase64BlockResults() != null
                    && handler.getDocBase64BlockResults().size() > 0) {
                StringBuffer sb=new StringBuffer();
                for (String item : handler.getDocBase64BlockResults()) {
                    sb.append(item+"\n");
                }
                data.put("base64Content",sb.toString());
            }
            if (handler.getXmlImgRefs() != null
                    && handler.getXmlImgRefs().size() > 0) {
                StringBuffer sb=new StringBuffer();
                for (String item : handler.getXmlImgRefs()) {
                    sb.append(item + "\n");
                }
                data.put("imageRef",sb.toString());
            }

//            模板替換
            ServletContext context = request.getServletContext();
            String rootPath = request.getRealPath("/");

            Configuration configuration = new Configuration();
            configuration.setDefaultEncoding("utf-8");
            configuration.setServletContextForTemplateLoading(context, "/freemarkTemplate");
            Template template = configuration.getTemplate("blankMht.ftl", "utf-8");

            String exportPath = rootPath + "export" + File.separator;
            File file1 = new File(exportPath);
            if (!file1.exists()) {
                file1.mkdirs();
            }
            Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(exportPath + "test1.doc"))));
//            WordHtmlGeneratorHelper.handleAllObject(data);
            template.process(data, out, ObjectWrapper.BEANS_WRAPPER);
//            ==================================================

            String filepath = exportPath + "test1.doc";
            resultMap.put("filepath", filepath);
            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }
        return null;
    }

這裡有一個需要注意的點:就是RichHtmlHandler物件設定的那幾個屬性值與模板中的對應屬性值一定要對應,否則結果不正確!

RichHtmlHandler類是將html內容處理為.mht可以接受的幾部分內容的工具類:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.jessica.word_mht.util.JFileUtils;
import com.jessica.word_mht.util.RequestResponseContext;
import com.jessica.word_mht.util.UUIDUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import javax.servlet.http.HttpServletRequest;

//import sun.tools.tree.NewArrayExpression;

/**
 * @Description:富文字Html處理器,主要處理圖片及編碼
 * @author:LiaoFei
 * @date :2016-3-28 下午4:13:21
 * @version V1.0
 * 
 */
public class RichHtmlHandler {

	private Document doc = null;
	private String html;

	private String docSrcParent = "";
	private String docSrcLocationPrex = "";
	private String nextPartId;
	private String shapeidPrex;
	private String spidPrex;
	private String typeid;

	private String handledDocBodyBlock;
	private List<String> docBase64BlockResults = new ArrayList<String>();
	private List<String> xmlImgRefs = new ArrayList<String>();

	public String getDocSrcLocationPrex() {
		return docSrcLocationPrex;
	}

	public void setDocSrcLocationPrex(String docSrcLocationPrex) {
		this.docSrcLocationPrex = docSrcLocationPrex;
	}

	public String getNextPartId() {
		return nextPartId;
	}

	public void setNextPartId(String nextPartId) {
		this.nextPartId = nextPartId;
	}

	public String getHandledDocBodyBlock() {
		String raw=   WordHtmlGeneratorHelper.string2Ascii(doc.getElementsByTag("body").html());
		return raw.replace("=3D", "=").replace("=", "=3D");
	}
	
	public String getRawHandledDocBodyBlock() {
		String raw=  doc.getElementsByTag("body").html();
		return raw.replace("=3D", "=").replace("=", "=3D");
	}
	public List<String> getDocBase64BlockResults() {
		return docBase64BlockResults;
	}

	public List<String> getXmlImgRefs() {
		return xmlImgRefs;
	}

	public String getShapeidPrex() {
		return shapeidPrex;
	}

	public void setShapeidPrex(String shapeidPrex) {
		this.shapeidPrex = shapeidPrex;
	}

	public String getSpidPrex() {
		return spidPrex;
	}

	public void setSpidPrex(String spidPrex) {
		this.spidPrex = spidPrex;
	}

	public String getTypeid() {
		return typeid;
	}

	public void setTypeid(String typeid) {
		this.typeid = typeid;
	}

	public String getDocSrcParent() {
		return docSrcParent;
	}

	public void setDocSrcParent(String docSrcParent) {
		this.docSrcParent = docSrcParent;
	}

	public String getHtml() {
		return html;
	}

	public void setHtml(String html) {
		this.html = html;
	}

	public RichHtmlHandler(String html) {
		doc = Jsoup.parse(	wrappHtml(html));
	}
	
	public void re_init(String html){
		doc=null;
		doc = Jsoup.parse(wrappHtml(html));
		docBase64BlockResults.clear();
		xmlImgRefs.clear();
	}
	
	/**
	 * @Description: 獲得已經處理過的HTML檔案
	 * @param @return
	 * @return String
	 * @throws IOException 
	 * @throws
	 * @author:LiaoFei
	 * @date:2016-3-28 下午4:16:34
	 */
	public void handledHtml(boolean isWebApplication, HttpServletRequest request)
			throws IOException {
		Elements imags = doc.getElementsByTag("img");

		if (imags == null || imags.size() == 0) {
			// 返回編碼後字串
			return;
			//handledDocBodyBlock = WordHtmlGeneratorHelper.string2Ascii(html);
		}

		// 轉換成word mht 能識別圖片標籤內容,去替換html中的圖片標籤

		for (Element item : imags) {
			// 把檔案取出來
			String src = item.attr("src").replace("3D\"","").replace("\"","");
			String srcRealPath = src;
			
			if (isWebApplication) {
//				String contentPath= RequestResponseContext.getRequest().getContextPath();
				String contentPath= request.getContextPath();
				if(!StringUtils.isEmpty(contentPath)){
					if(src.startsWith(contentPath)){
						src=src.substring(contentPath.length());
					}
				}
				
//				srcRealPath = RequestResponseContext.getRequest().getSession()
//						.getServletContext().getRealPath(src);
				srcRealPath = request.getSession()
						.getServletContext().getRealPath(src);
				
			}
			
			File imageFile = new File(srcRealPath);
			String imageFielShortName = imageFile.getName();
			String fileTypeName = JFileUtils.getFileSuffix(srcRealPath);

			String docFileName = "image" + UUIDUtils.get32UUID() + "."
					+ fileTypeName;
			String srcLocationShortName = docSrcParent + "/" + docFileName;

			String styleAttr = item.attr("style"); // 樣式
			//高度
			String imagHeightStr=item.attr("height");;
			if(StringUtils.isEmpty(imagHeightStr)){
				imagHeightStr = getStyleAttrValue(styleAttr, "height");
			}
			//寬度
			String imagWidthStr=item.attr("width");;
			if(StringUtils.isEmpty(imagHeightStr)){
				imagHeightStr = getStyleAttrValue(styleAttr, "width");
			}
	
			imagHeightStr = imagHeightStr.replace("px", "");
			imagWidthStr = imagWidthStr.replace("px", "");
			if(StringUtils.isEmpty(imagHeightStr)){
				//去得到預設的檔案高度
				imagHeightStr="0";
			}
			if(StringUtils.isEmpty(imagWidthStr)){
				imagWidthStr="0";
			}
			int imageHeight = Integer.parseInt(imagHeightStr);
			int imageWidth = Integer.parseInt(imagWidthStr);
			
			// 得到檔案的word mht的body塊
			String handledDocBodyBlock = WordImageConvertor.toDocBodyBlock(srcRealPath,
					imageFielShortName, imageHeight, imageWidth,styleAttr,
					srcLocationShortName, shapeidPrex, spidPrex, typeid);

			item.parent().append(handledDocBodyBlock);
			item.remove();
			// 去替換原生的html中的imag

			String base64Content = WordImageConvertor
					.imageToBase64(srcRealPath);
			String contextLoacation = docSrcLocationPrex + "/" + docSrcParent
					+ "/" + docFileName;

			String docBase64BlockResult = WordImageConvertor
					.generateImageBase64Block(nextPartId, contextLoacation,
							fileTypeName, base64Content);
			docBase64BlockResults.add(docBase64BlockResult);

			String imagXMLHref = "<o:File HRef=3D\"" + docFileName + "\"/>";
			xmlImgRefs.add(imagXMLHref);

		}

	}

	private String getStyleAttrValue(String style, String attributeKey) {
		if (StringUtils.isEmpty(style)) {
			return "";
		}

		// 以";"分割
		String[] styleAttrValues = style.split(";");
		for (String item : styleAttrValues) {
			// 在以 ":"分割
			String[] keyValuePairs = item.split(":");
			if (attributeKey.equals(keyValuePairs[0])) {
				return keyValuePairs[1];
			}
		}

		return "";
	}
	
	private String wrappHtml(String html){
		// 因為傳遞過來都是不完整的doc
		StringBuilder sb = new StringBuilder();
		sb.append("<html>");
		sb.append("<body>");
		sb.append(html);

		sb.append("</body>");
		sb.append("</html>");
		return sb.toString();
	}
	

}

另外依賴的工具類:

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.math.BigDecimal;

import javax.imageio.ImageIO;

import com.jessica.word_mht.util.UUIDUtils;
import org.apache.commons.codec.binary.Base64;

import sun.misc.BASE64Encoder;



/**   
* @Description:WORD 文件圖片轉換器
* @author:LiaoFei  
* @date :2016-3-28 上午11:21:06  
* @version V1.0  
*   
*/
public class WordImageConvertor {
	
	//private static Const WORD_IMAGE_SHAPE_TYPE_ID="";
	
	/**   
	* @Description: 將圖片轉換成base64編碼的字串  
	* @param @param imageSrc 檔案路徑
	* @param @return    
	* @return String   
	 * @throws IOException 
	 * @throws
	* @author:LiaoFei
	* @date:2016-3-28 上午11:22:26
	*/ 
	public static String imageToBase64(String imageSrc) throws IOException{
		//判斷檔案是否存在
		File file=new File(imageSrc);
		if(!file.exists()){
			throw new FileNotFoundException("檔案不存在!");
		}
		StringBuilder pictureBuffer = new StringBuilder();
		FileInputStream input=new FileInputStream(file);
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        
		//讀取檔案
		
		//BufferedInputStream bi=new BufferedInputStream(in);
        Base64 base64=new Base64();
		BASE64Encoder encoder=new BASE64Encoder();
		byte[] temp = new byte[1024];
        for(int len = input.read(temp); len != -1;len = input.read(temp)){
            out.write(temp, 0, len);
            //out(pictureBuffer.toString());
            //out.reset();
        }
        pictureBuffer.append(new String( base64.encodeBase64Chunked(out.toByteArray())));
        //pictureBuffer.append(encoder.encodeBuffer(out.toByteArray()));
        
        
		/*byte[] data=new byte[input.available()];
        input.read(data);
        pictureBuffer.append(base64.encodeBase64String (data));*/
        
        input.close();
        /*BASE64Decoder decoder=new BASE64Decoder();
		FileOutputStream write = new FileOutputStream(new File("c:\\test2.jpg"));
        //byte[] decoderBytes = decoder.decodeBuffer (pictureBuffer.toString());
		byte[] decoderBytes = base64.decodeBase64(pictureBuffer.toString());
        write.write(decoderBytes);
        write.close();*/
		
		return pictureBuffer.toString();
	}
	
	
	
	public static String toDocBodyBlock(
			String imageFilePath,
			String imageFielShortName,
			int imageHeight,
			int imageWidth,
			String imageStyle,
			String srcLocationShortName,
			String shapeidPrex,String spidPrex,String typeid){
		//shapeid
		//mht檔案中針對shapeid的生成好像規律,其內建的生成函式沒法得知,但是隻要保證其唯一就行
		//這裡用前置加32位的uuid來保證其唯一性。
		String shapeid=shapeidPrex;
		shapeid+= UUIDUtils.get32UUID();
		
		//spid ,同shapeid處理
		String spid=spidPrex;
		spid+=UUIDUtils.get32UUID();
		
		
	/*	<!--[if gte vml 1]><v:shape id=3D"_x56fe__x7247__x0020_0" o:spid=3D"_x0000_i10=
				26"
				   type=3D"#_x0000_t75" alt=3D"725017921264249223.jpg" style=3D'width:456.7=
				5pt;
				   height:340.5pt;visibility:visible;mso-wrap-style:square'>
				   <v:imagedata src=3D"file9462.files/image001.jpg" o:title=3D"725017921264=
				249223"/>
				  </v:shape><![endif]--><![if !vml]><img width=3D609 height=3D454
				  src=3D"file9462.files/image002.jpg" alt=3D725017921264249223.jpg v:shapes=
				=3D"_x56fe__x7247__x0020_0"><![endif]>*/
		StringBuilder sb1=new StringBuilder();
		
		sb1.append(" <!--[if gte vml 1]>");
		sb1.append("<v:shape id=3D\"" + shapeid+"\"");
		sb1.append("\n");
		sb1.append(" o:spid=3D\""+ spid +"\"" );
		sb1.append(" type=3D\""+  typeid +"\" alt=3D\"" + imageFielShortName +"\"");
		sb1.append("\n");
		sb1.append( " style=3D' " + generateImageBodyBlockStyleAttr(imageFilePath,imageHeight,imageWidth) + imageStyle +"'");
		sb1.append(">");
		sb1.append("\n");
		sb1.append(" <v:imagedata src=3D\"" + srcLocationShortName +"\""  );
		sb1.append("\n");
		sb1.append(" o:title=3D\"" + imageFielShortName.split("\\.")[0]+"\""  );
		sb1.append("/>");
		sb1.append("</v:shape>");
		sb1.append("<![endif]-->");
		
		//以下是為了相容遊覽器顯示時的效果,但是如果是純word閱讀的話沒必要這麼做。
	/*	StringBuilder sb2=new StringBuilder();
		sb2.append(" <![if !vml]>");
		
		sb2.append("<img width=3D"+imageWidth +" height=3D" +imageHeight +
				  " src=3D\"" + srcLocationShortName +"\" alt=" +imageFielShortName+
				  " v:shapes=3D\"" + shapeid +"\">");
		
		sb2.append("<![endif]>");*/
		
		//return sb1.toString()+sb2.toString();
		return sb1.toString();
	}
	
	/**   
	* @Description: 生成圖片的base4塊  
	* @param @param nextPartId
	* @param @param contextLoacation
	* @param @param ContentType
	* @param @param base64Content
	* @param @return    
	* @return String    
	* @throws
	* @author:LiaoFei
	* @date:2016-3-28 下午4:02:05
	*/ 
	public static String generateImageBase64Block(String nextPartId,String contextLoacation,
									String fileTypeName,String base64Content){
		/*--=_NextPart_01D188DB.E436D870
				Content-Location: file:///C:/70ED9946/file9462.files/image001.jpg
				Content-Transfer-Encoding: base64
				Content-Type: image/jpeg
				
				base64Content
		*/
		
		StringBuilder sb=new StringBuilder();
		sb.append("\n");
		sb.append("\n");
		sb.append("------=_NextPart_"+nextPartId);
		sb.append("\n");
		sb.append("Content-Location: "+ contextLoacation);
		sb.append("\n");
		sb.append("Content-Transfer-Encoding: base64");
		sb.append("\n");
		sb.append("Content-Type: " + getImageContentType(fileTypeName));
		sb.append("\n");
		sb.append("\n");
		sb.append(base64Content);
		
		return sb.toString();
	}
	
	
	private static String generateImageBodyBlockStyleAttr(String imageFilePath, int height,int width){
		StringBuilder sb=new StringBuilder();
		
		BufferedImage sourceImg;
		try {
			sourceImg = ImageIO.read(new FileInputStream(imageFilePath));
			if(height==0){
				height=sourceImg.getHeight();
			}
			if(width==0){
				width=sourceImg.getWidth();
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
		//將畫素轉化成pt 
	    BigDecimal heightValue=new BigDecimal(height*12/16);
	    heightValue= heightValue.setScale(2, BigDecimal.ROUND_HALF_UP);
	    BigDecimal widthValue=new BigDecimal(width*12/16);
	    widthValue= widthValue.setScale(2, BigDecimal.ROUND_HALF_UP);
	  
		sb.append("height:"+heightValue +"pt;");
		sb.append("width:"+widthValue +"pt;");
		sb.append("visibility:visible;");
		sb.append("mso-wrap-style:square; ");
		
		
		return sb.toString();
	}
	
	private static String getImageContentType(String fileTypeName){
		String result="image/jpeg";
		//http://tools.jb51.net/table/http_content_type
		if(fileTypeName.equals("tif") || fileTypeName.equals("tiff")){
			result="image/tiff";
		}else if(fileTypeName.equals("fax")){
			result="image/fax";
		}else if(fileTypeName.equals("gif")){
			result="image/gif";
		}else if(fileTypeName.equals("ico")){
			result="image/x-icon";
		}else if(fileTypeName.equals("jfif") || fileTypeName.equals("jpe") 
					||fileTypeName.equals("jpeg")  ||fileTypeName.equals("jpg")){
			result="image/jpeg";
		}else if(fileTypeName.equals("net")){
			result="image/pnetvue";
		}else if(fileTypeName.equals("png") || fileTypeName.equals("bmp") ){
			result="image/png";
		}else if(fileTypeName.equals("rp")){
			result="image/vnd.rn-realpix";
		}else if(fileTypeName.equals("rp")){
			result="image/vnd.rn-realpix";
		}
		
		return result;
	}
	
	
	public static void main(String[] args) throws FileNotFoundException, IOException {
		/*String picture="F:\\725017921264249223.jpg";
		BufferedImage sourceImg =ImageIO.read(new FileInputStream(picture));
		int height=sourceImg.getHeight();
		int width=sourceImg.getWidth();
		System.out.println(height +" And pt=" +height*12/16);
		System.out.println(width+" And pt=" +width*12/16);*/
		
		String picture="E:\\huoqubing.jpg";
		System.out.println(imageToBase64(picture));
		
		 
	}

}

 至此,成功匯出為word:

 

另外,之前只用國外開源的專案docx-html-editor也成功匯出了帶圖片的word,但是原始碼中沒有使用spring框架,不好新增到現有專案中,有興趣的小夥伴可以移步去嘗試下,成功了記得告訴我!

傳送門:https://github.com/plutext/docx-html-editor