在百度富文本編輯器UEditor中增加word轉(zhuǎn)html的方法

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去做了一些交互。


ueditor.all.js&ueditor.parse.js

我打算在上傳附件時(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ò)去了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容