在百度富文本编辑器UEditor中增加word转html的方法

1.需求

在一个项目中有个需求:复制word的内容到编辑器中。但是在复制过程中图片不能成功的复制过来,需要安装flash插件,但是吧又不能要求每个客户都安装上,这就比较麻烦了。所以考虑是不是可以把word转成html,这样放入编辑器中打开就没问题了把。

2.准备

麻烦的又来了,UEditor1.4之后才支持简便的二次开发扩展,但项目使用了老版本的UEditor,全面替换吧肯定又是一堆坑,没办法就只能改源码,顺便在这里记录一下。

3.实施

研究了一通之后发现主要业务逻辑写在了ueditor.all.js&ueditor.parse.js这两个js中,同时有大量的dialog去做了一些交互。


ueditor.all.js&ueditor.parse.js

我打算在上传附件时增加一个tab页去专门处理转换的工作,这样可以做到改动比较小。

3.1 增加上传附件并转换

UEditor的上传附件交互主要在dialog下的attachment文件夹,包括了attachment.html;attachment.css;attachment.js三个文件,下面依次来改下。

3.1.1 更改attachment.html

参照原来的tab页增加一个我们自己的:

<span class="tab" data-content-id="transform"><var id="lang_tab_transform"></var></span>

Tab页对应的展示块:

            <!-- 上传文件 -->
            <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>

要想我们新增的内容匹配到文字描述,这里要配置下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转Html',
 'lang_word_upload':"Word上传",

3.1.2 更改attachment.css

再添加相应的css,这个比较简单把原来upload的css复制一份,把"upload"改成我自己定义的"transform"添加进去即可。太多太长这里就不贴了。

3.1.3 更改attachment.js

首先增加一个对象transFile:

 var uploadFile,
        transFile,
        onlineFile;

在设置tab的时候增加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确认按钮时增加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个未上传文件'.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);
        };
    }

我不想写冗余的代码(主要是懒:)所以想复用原来上传的逻辑,但是原来代码里很多写死的ID,所以要改一下:
增加了个target记住当前的页面是哪个。

    /* 上传附件 */
    function UploadFile(target) {
        this.target = target;
        this.$wrap = target.constructor == String ? $('#' + target) : $(target);
        this.init();
    }

在定义中新定义了一些ID:

     //设置不同ID
     filePickerBtnId = 'filePickerBtn',
     filePickerReadyId = 'filePickerReady',
     filePickerBlockId = 'filePickerBlock',

判断当前页面是不是我新加的,是的话变更值:

  //如果为转换上传重新设置相关ID
  if ("queueListTrans"==this.target){
        filePickerBtnId = 'filePickerBtnTrans';
        filePickerReadyId = 'filePickerReadyTrans';
        filePickerBlockId = 'filePickerBlockTrans';
  }

把下面用到写死的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
            });

最后的效果便是这个样子的了:


前端请求后端的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 实现后端的word转换html的服务

本来想用SpringMVC实现,看了下系统的架构还是老老实实的写个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", "参数不合法");
        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());
    }

    /**
     * 转换doc格式word为html
     */
    private JSONObject wordTransformHtml(String type, String filePath) {
        JSONObject resultJson = new JSONObject();
        resultJson.put("code", "1");
        resultJson.put("msg", "转换失败");
        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", "转换成功");
            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转换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转换html
    public String docxToHtml(String fileName) throws IOException {
        String htmlStr = null;
        try{
            XWPFDocument docxDocument = new XWPFDocument(new FileInputStream(fileName));
            XHTMLOptions options = XHTMLOptions.create();
            //图片转base64
            options.setImageManager(new Base64EmbedImgManager());
            // 转换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.总结

至此,便通过把word转换成html实现了复制word的内容到编辑器中的功能,图片也完美的复制过去了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容