1.需求
在一個(gè)項(xiàng)目中有個(gè)需求:復(fù)制word的內(nèi)容到編輯器中。但是在復(fù)制過(guò)程中圖片不能成功的復(fù)制過(guò)來(lái),需要安裝flash插件,但是吧又不能要求每個(gè)客戶都安裝上,這就比較麻煩了。所以考慮是不是可以把word轉(zhuǎn)成html,這樣放入編輯器中打開(kāi)就沒(méi)問(wèn)題了把。
2.準(zhǔn)備
麻煩的又來(lái)了,UEditor1.4之后才支持簡(jiǎn)便的二次開(kāi)發(fā)擴(kuò)展,但項(xiàng)目使用了老版本的UEditor,全面替換吧肯定又是一堆坑,沒(méi)辦法就只能改源碼,順便在這里記錄一下。
3.實(shí)施
研究了一通之后發(fā)現(xiàn)主要業(yè)務(wù)邏輯寫(xiě)在了ueditor.all.js&ueditor.parse.js這兩個(gè)js中,同時(shí)有大量的dialog去做了一些交互。
我打算在上傳附件時(shí)增加一個(gè)tab頁(yè)去專門處理轉(zhuǎn)換的工作,這樣可以做到改動(dòng)比較小。
3.1 增加上傳附件并轉(zhuǎn)換
UEditor的上傳附件交互主要在dialog下的attachment文件夾,包括了attachment.html;attachment.css;attachment.js三個(gè)文件,下面依次來(lái)改下。
3.1.1 更改attachment.html
參照原來(lái)的tab頁(yè)增加一個(gè)我們自己的:
<span class="tab" data-content-id="transform"><var id="lang_tab_transform"></var></span>
Tab頁(yè)對(duì)應(yīng)的展示塊:
<!-- 上傳文件 -->
<div id="transform" class="panel focus">
<div id="queueListTrans" class="queueList">
<div class="statusBar element-invisible">
<div class="progress">
<span class="text">0%</span>
<span class="percentage"></span>
</div><div class="info"></div>
<div class="btns">
<div id="filePickerBtnTrans"></div>
<div class="uploadBtn"><var id="lang_word_upload"></var></div>
</div>
</div>
<div id="dndAreaTrans" class="placeholder">
<div class="filePickerContainer">
<div id="filePickerReadyTrans"></div>
</div>
</div>
<ul class="filelist element-invisible">
<li id="filePickerBlockTrans" class="filePickerBlock"></li>
</ul>
</div>
</div>
要想我們新增的內(nèi)容匹配到文字描述,這里要配置下zh-cn.js和en.js
在en.js的'attachment':'static':中增加:
'lang_tab_transform': 'Word Trans Html',
'lang_word_upload':"Word upload",
在zh-cn.js的'attachment':'static':中增加:
'lang_tab_transform': 'Word轉(zhuǎn)Html',
'lang_word_upload':"Word上傳",
3.1.2 更改attachment.css
再添加相應(yīng)的css,這個(gè)比較簡(jiǎn)單把原來(lái)upload的css復(fù)制一份,把"upload"改成我自己定義的"transform"添加進(jìn)去即可。太多太長(zhǎng)這里就不貼了。
3.1.3 更改attachment.js
首先增加一個(gè)對(duì)象transFile:
var uploadFile,
transFile,
onlineFile;
在設(shè)置tab的時(shí)候增加tansFile:
switch (id) {
case 'upload':
uploadFile = uploadFile || new UploadFile('queueList');
break;
case 'transform':
transFile = transFile || new UploadFile('queueListTrans');
break;
case 'online':
onlineFile = onlineFile || new OnlineFile('fileList');
break;
}
初始化dialog確認(rèn)按鈕時(shí)增加transFile:
/* 初始化onok事件 */
function initButtons() {
dialog.onok = function () {
var list = [], id, tabs = $G('tabhead').children;
for (var i = 0; i < tabs.length; i++) {
if (domUtils.hasClass(tabs[i], 'focus')) {
id = tabs[i].getAttribute('data-content-id');
break;
}
}
switch (id) {
case 'upload':
list = uploadFile.getInsertList();
var count = uploadFile.getQueueCount();
if (count) {
$('.info', '#queueList').html('<span style="color:red;">' + '還有2個(gè)未上傳文件'.replace(/[\d]/, count) + '</span>');
return false;
}
editor.execCommand('insertfile', list);
break;
case 'transform':
list = transFile.getInsertList();
console.log("transFileUrl:"+list[0].url);
var wordHtml = wordTransHtml(list[0].url);
console.log("wordHtml:"+wordHtml);
editor.execCommand('insertHTML', wordHtml);
break;
case 'online':
list = onlineFile.getInsertList();
editor.execCommand('insertfile', list);
break;
}
// editor.execCommand('insertfile', list);
};
}
我不想寫(xiě)冗余的代碼(主要是懶:)所以想復(fù)用原來(lái)上傳的邏輯,但是原來(lái)代碼里很多寫(xiě)死的ID,所以要改一下:
增加了個(gè)target記住當(dāng)前的頁(yè)面是哪個(gè)。
/* 上傳附件 */
function UploadFile(target) {
this.target = target;
this.$wrap = target.constructor == String ? $('#' + target) : $(target);
this.init();
}
在定義中新定義了一些ID:
//設(shè)置不同ID
filePickerBtnId = 'filePickerBtn',
filePickerReadyId = 'filePickerReady',
filePickerBlockId = 'filePickerBlock',
判斷當(dāng)前頁(yè)面是不是我新加的,是的話變更值:
//如果為轉(zhuǎn)換上傳重新設(shè)置相關(guān)ID
if ("queueListTrans"==this.target){
filePickerBtnId = 'filePickerBtnTrans';
filePickerReadyId = 'filePickerReadyTrans';
filePickerBlockId = 'filePickerBlockTrans';
}
把下面用到寫(xiě)死的ID的地方替換成我定義的變量:
if (!WebUploader.Uploader.support()) {
$('#'+filePickerReadyId).after($('<div>').html(lang.errorNotSupport)).hide();
return;
} else if (!editor.getOpt('fileActionName')) {
$('#'+filePickerReadyId).after($('<div>').html(lang.errorLoadConfig)).hide();
return;
}
uploader = _this.uploader = WebUploader.create({
pick: {
id: '#'+filePickerReadyId,
label: lang.uploadSelectFile
},
swf: '../../third-party/webuploader/Uploader.swf',
server: actionUrl,
fileVal: editor.getOpt('fileFieldName'),
duplicate: true,
fileSingleSizeLimit: fileMaxSize,
compress: false
});
uploader.addButton({
id: '#'+filePickerBlockId
});
uploader.addButton({
id: '#'+filePickerBtnId,
label: lang.uploadAddFile
});
最后的效果便是這個(gè)樣子的了:
前端請(qǐng)求后端的ajax方法:
function wordTransHtml(path) {
console.log("In wordTransHtml:"+path);
$.ajax({
type:"GET",
url:"../../../WordTransHtml",
data:{type:"docx",path:path},
success:function (data) {
console.log("wordTransHtml data:"+data);
console.log(data);
console.log("wordTransHtml data.body:"+data.body);
// $('#token').html(data);
},
error: function (err) {
$('#token').html(err);
}
});
}
3.2 實(shí)現(xiàn)后端的word轉(zhuǎn)換html的服務(wù)
本來(lái)想用SpringMVC實(shí)現(xiàn),看了下系統(tǒng)的架構(gòu)還是老老實(shí)實(shí)的寫(xiě)個(gè)servlet吧。(:
3.2.1 所需jar包如下:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.poi.xwpf.converter.xhtml</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.0</version>
</dependency>
3.2.2 后端Servlet
import com.alibaba.fastjson.JSONObject;
import com.ztesoft.util.WordToHtmlUtil;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
public class WordTransHtmlServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("In WordTransHtmlServlet.");
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
JSONObject resultJson = new JSONObject();
resultJson.put("code", "2");
resultJson.put("msg", "參數(shù)不合法");
resultJson.put("htmlStr", "");
try {
String path = request.getParameter("path");
path = System.getProperty("user.dir") + "/webapps" + path;
String[] paths = path.split("\\.");
System.out.println("WordTransHtmlServlet paths:" + paths + " length:" + paths.length);
System.out.println("WordTransHtmlServlet Class1:" + System.getProperty("user.dir"));
System.out.println("WordTransHtmlServlet Class2:" + this.getClass().getResource("/").getPath());
System.out.println("WordTransHtmlServlet Class3:" + request.getSession().getServletContext().getRealPath("../"));
String type = "";
if (paths.length > 1) type = paths[1];
System.out.println("WordTransHtmlServlet type:" + type + " path:" + path);
resultJson = wordTransformHtml(type, path);
} catch (Exception e) {
e.printStackTrace();
}
PrintWriter out = response.getWriter();
out.write(resultJson.toString());
}
/**
* 轉(zhuǎn)換doc格式word為html
*/
private JSONObject wordTransformHtml(String type, String filePath) {
JSONObject resultJson = new JSONObject();
resultJson.put("code", "1");
resultJson.put("msg", "轉(zhuǎn)換失敗");
resultJson.put("html", "");
try {
String wordHtml = "";
WordToHtmlUtil wordToHtmlUtil = new WordToHtmlUtil();
if ("doc".equals(type)) {
wordHtml = wordToHtmlUtil.docToHtml(filePath);
} else if ("docx".equals(type)) {
wordHtml = wordToHtmlUtil.docxToHtml(filePath);
}
System.out.println("docTransformHtml wordHtml:" + wordHtml);
resultJson.put("code", "0");
resultJson.put("msg", "轉(zhuǎn)換成功");
resultJson.put("html", wordHtml);
} catch (Exception e) {
e.printStackTrace();
}
return resultJson;
}
/**
* 獲取文件
*/
private void getFile(String fileName) {
try {
String path = System.getProperty("user.dir");
System.out.println("path1:" + path);
path = path.replaceAll("\\\\", "/");
System.out.println("path1:" + path);
String filePath = path + "/webapp" + fileName;
System.out.println("filePath:" + filePath);
File file = new File(filePath);
PrintFileContext(file);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void PrintFileContext(File file) throws Exception {
InputStream in = new FileInputStream(file);
InputStreamReader isr = new InputStreamReader(in);
BufferedReader br = new BufferedReader(isr);
String line = "";
while (true) {
line = br.readLine();
if (line == null) {
break;
}
System.out.println(line);
}
br.close();
}
}
3.2.3 后端Word轉(zhuǎn)換Html的工具
import fr.opensagres.poi.xwpf.converter.xhtml.Base64EmbedImgManager;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLConverter;
import fr.opensagres.poi.xwpf.converter.xhtml.XHTMLOptions;
import org.apache.commons.io.FileUtils;
import org.apache.poi.hwpf.HWPFDocumentCore;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.hwpf.converter.WordToHtmlUtils;
import org.apache.poi.hwpf.usermodel.Picture;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Base64;
public class WordToHtmlUtil {
public String docToHtml(String fileName) throws IOException, ParserConfigurationException, TransformerException {
String htmlStr = null;
try{
HWPFDocumentCore wordDocument = WordToHtmlUtils.loadDoc(new FileInputStream(fileName));
WordToHtmlConverter wordToHtmlConverter = new ImageConverter(
DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()
);
wordToHtmlConverter.processDocument(wordDocument);
Document htmlDocument = wordToHtmlConverter.getDocument();
ByteArrayOutputStream out = new ByteArrayOutputStream();
DOMSource domSource = new DOMSource(htmlDocument);
StreamResult streamResult = new StreamResult(out);
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer serializer = transformerFactory.newTransformer();
serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
serializer.setOutputProperty(OutputKeys.INDENT, "yes");
serializer.setOutputProperty(OutputKeys.METHOD, "html");
serializer.transform(domSource, streamResult);
out.close();
htmlStr = new String(out.toByteArray());
}catch (Exception e){
e.printStackTrace();
}
return htmlStr;
}
//docx轉(zhuǎn)換html
public String docxToHtml(String fileName) throws IOException {
String htmlStr = null;
try{
XWPFDocument docxDocument = new XWPFDocument(new FileInputStream(fileName));
XHTMLOptions options = XHTMLOptions.create();
//圖片轉(zhuǎn)base64
options.setImageManager(new Base64EmbedImgManager());
// 轉(zhuǎn)換htm1
ByteArrayOutputStream htmlStream = new ByteArrayOutputStream();
XHTMLConverter.getInstance().convert(docxDocument, htmlStream, options);
htmlStr = htmlStream.toString();
}catch (Exception e){
e.printStackTrace();
}
return htmlStr;
}
public class ImageConverter extends WordToHtmlConverter {
public ImageConverter(Document document) {
super(document);
}
@Override
protected void processImageWithoutPicturesManager(Element currentBlock, boolean inlined, Picture picture){
try{
Element imgNode = currentBlock.getOwnerDocument().createElement("img");
StringBuffer sb = new StringBuffer();
sb.append(Base64.getMimeEncoder().encodeToString(picture.getRawContent()));
sb.insert(0, "data:" + picture.getMimeType() + ";base64,");
imgNode.setAttribute("src", sb.toString());
currentBlock.appendChild(imgNode);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.總結(jié)
至此,便通過(guò)把word轉(zhuǎn)換成html實(shí)現(xiàn)了復(fù)制word的內(nèi)容到編輯器中的功能,圖片也完美的復(fù)制過(guò)去了。