使用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'>测试报告</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'>一、<span style=3D'font:7.0pt "Times New Roman"'> </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'>概要</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'>对于导出<span lang=3DEN-US>Word </span>并携带图片的<span lang=3DEN-US>123</span> 种方法探索,查 询到使用<span lang=3DEN-US>itext</span>转换信息<span lang=3DEN-US>${num1!}</span>条,使用<span lang=3DEN-US>openoffice</span>转换信息<span lang=3DEN-US>${num2!}</span>条,<span lang=3DEN-US>pageoffice</span>转换信息<span lang=3DEN-US>${num3!}</span>条,<span lang=3DEN-US>poi</span>转换信息<span lang=3DEN-US>${num4!}</span>条,<span lang=3DEN-US>freemarker</span>转换信息<span lang=3DEN-US>${num5!}</span>条。<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'>(一)<span style=3D'font:7.0pt "Times New Roman"'> </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'>表格展示</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'>统计人</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> </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'> 哈哈哈,成功了!<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> </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