• 记一次doc、docx转html的过程


    原因

    近期由于项目开发的需求,需要将系统的文件(.doc或者.docx)里面的内容转成html字符串,然后作为请求的参数中body发送出去。
    为了实现这个功能,前期踩了很多坑,试错了很多就能勉强实现功能需求。所以怎么也要记录一下,以免自己后续继续踩坑,以及大伙也跳进来。

    方案

    最开始,在网上搜了很多方案,主要归纳成就是通过三种方式,但是三种方法中都需要引入外部的工具jar包。
    最后综合了一下目前所开发的系统内容,决定采用使用POI实现转换。

    前期准备

    决定使用POI来实现功能,就要找到合适的依赖。在这个过程也是出现很多问题,就是各个依赖包的版本不同,会出现某一类不存在的问题。为了不必要的麻烦,这里直接以poi的版本为4.1.2为准则,其他依赖包来兼容这个版本。

    <dependency>
        <groupId>org.apache.poigroupId>
        <artifactId>poi-scratchpadartifactId>
        <version>4.1.2version>
    dependency>
    <dependency>
        <groupId>org.apache.poigroupId>
        <artifactId>poi-ooxmlartifactId>
        <version>4.1.2version>
    dependency>
    <dependency>
        <groupId>org.apache.poigroupId>
        <artifactId>poiartifactId>
        <version>4.1.2version>
    dependency>
    <dependency>
        <groupId>org.apache.poigroupId>
        <artifactId>poi-ooxml-schemasartifactId>
        <version>4.1.2version>
    dependency>
    
    <dependency>
         <groupId>fr.opensagres.xdocreportgroupId>
         <artifactId>fr.opensagres.poi.xwpf.converter.xhtmlartifactId>
         <version>2.0.2version>
     dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    注意:不要随便改变包的版本,不然就要重新去验证是否兼不兼容、部分类是否缺失。

    代码核心部分

    这里,只是提出doc/docx转换成html的方法,但是里面的一些额外的函数/方法则只是描述其实现什么功能而已。

    /**
     1. 将doc或docx文件转成html字符串
     2. @param ips 文件流
     3. @param fileName 文件名
     4. @param extension 后缀名
     5. @return html字符串
     6.  7. 若文档中存在图片,则需要设置图片的保存目录,以及图片服务器访问路径才能正常使用
     */
    private String wordToHtml(InputStream ips, String fileName, String extension){
    
        File file = null;
    
        try {
            if (ips == null){
                return "";
            }
            //判断文档为doc或docx,分别进行处理转换
            if (extension.equals(".doc") || extension.equals(".DOC")){
                HWPFDocument wordDocument = new HWPFDocument(ips);
                WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
                        DocumentBuilderFactory.newInstance().newDocumentBuilder()
                                .newDocument());
                //该部分为doc中的图片操作(如doc中存在图片),可将图片上传服务器和获取图片URL的逻辑放在重写的位置
                //参数说明:savePicture(图片byte, 图片type, 图片名, 图片宽度, 图片高度)
                wordToHtmlConverter.setPicturesManager(new PicturesManager() {
                    @Override
                    public String savePicture(byte[] content, PictureType pictureType, String suggestedName, float widthInches, float heightInches) {
                        return uploadPicFile(...); //返回的图片URL即为在生成的html文件中图片的src
                    }
                });
                wordToHtmlConverter.processDocument(wordDocument);
    
                Document htmlDocument = wordToHtmlConverter.getDocument();
                ByteArrayOutputStream out = new ByteArrayOutputStream();
                DOMSource domSource = new DOMSource(htmlDocument);
                StreamResult streamResult = new StreamResult(out);
                TransformerFactory tf = TransformerFactory.newInstance();
                //设置转换参数
                Transformer serializer = tf.newTransformer();
                serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
                serializer.setOutputProperty(OutputKeys.INDENT, "yes");
                serializer.setOutputProperty(OutputKeys.METHOD, "html");
                serializer.transform(domSource, streamResult);
                String result = new String(out.toByteArray());
                out.close();
                result = result.replaceAll("\\r\\n","
    "
    ); log.info("doc文件转换后的字符串为:{}", result); return result; }else if (extension.equals(".docx") || extension.equals(".DOCX")){ file = inputstreamToFile(ips, fileName); XWPFDocument document = new XWPFDocument(new FileInputStream(file)); XHTMLOptions options = XHTMLOptions.create(); //若文档中存在图片,则将内容设置在option中的ImageManager //ImageManager imageManager = new ImageManager(new File(picPath), ""); options.setIgnoreStylesIfUnused(false); options.setFragment(true); //options.setImageManager(imageManager); //设置文档内图片储存的位置 options.setImageManager(new ImageManagerImpl()); //重写ImageManager类,用于构建图片路径 ByteArrayOutputStream out = new ByteArrayOutputStream(); XHTMLConverter.getInstance().convert(document, out, options); String result = new String(out.toByteArray()); log.info("docx文件转换后的字符串为:{}", result); out.close(); document.close(); return result; } }catch (Exception e){ log.error("word转html文件出现错误:{}", e); }finally { FileUtil.del(file); } return null; }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77

    代码描述:

    1. 该方法传入的参数有三个:被操作文件的文件流ips文件名fileName文件的后缀extension
    2. 重写的savePicture()方法作用是将doc中的图片构建成在生成的html文件中图片的src的url。
    3. savePicture()的参数说明:savePicture(图片byte, 图片type, 图片名, 图片宽度, 图片高度)
    4. uploadPicFile(…)方法是用来实现将图片的byte数组上传到图片服务器,并组装成一个完整可直接访问的路径返回。具体的实现过程就要根据小伙伴自己的需求做进一步开发了。
    5. 在处理docx文件的时候,由于我这边需要将图片上传到服务器,所以这边是采用重写ImageManager这个类来实现自己的上传逻辑。
    /**
     1. 重写ImageManager类,用于docx文档中的图片进行保存和替换访问url到html字符串中
     */
    public class ImageManagerImpl extends ImageManager {
    
        private byte[] picture; //图片的byte数组
        private String fileName; //文件名
    
        public ImageManagerImpl() {
            super(new File(""), "");
        }
    
        @Override
        public void extract(String imagePath, byte[] imageData) throws IOException {
            String[] split = imagePath.split("/");
            this.fileName = split[split.length - 1];
            this.picture = imageData;
        }
    
        @Override
        public String resolve(String uri) {
        	//可使用fileName和picture传入到uploadPicFile()方法中
            return uploadPicFile(...); //此处返回的应该是一个完整且可直接访问的图片url
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    1. 当然,如果你只想保存到本地的一个路径就要修改成下面的样子,可以看到在处理docx的时候,有注释掉的几行代码:
    ImageManager imageManager = new ImageManager(new File(picPath), "");
    options.setIgnoreStylesIfUnused(false);
    options.setFragment(true);
    options.setImageManager(imageManager); //设置文档内图片储存的位置
    
    • 1
    • 2
    • 3
    • 4

    其中,picPath为图片保存的本地路径。
    如果是处理doc文件,则可参考下面的代码

    File file = new File("E:\\temp\\ww.doc");
    FileInputStream fileInputStream = new FileInputStream(file);
    HWPFDocument wordDocument = new HWPFDocument(fileInputStream);
    WordToHtmlConverter wordToHtmlConverter = new WordToHtmlConverter(
            DocumentBuilderFactory.newInstance().newDocumentBuilder()
                    .newDocument());
    wordToHtmlConverter.setPicturesManager(new PicturesManager() {
        @Override
        public String savePicture(byte[] content,PictureType pictureType, String suggestedName,float widthInches, float heightInches) {
            return uploadPicFile(...);
        }
    });
    wordToHtmlConverter.processDocument(wordDocument);
    //获取doc文件中的所有图片,并保存到本地,picPath为本地路径
    List pics = wordDocument.getPicturesTable().getAllPictures();
    if (pics != null) {
        for (int i = 0; i < pics.size(); i++) {
            Picture pic = (Picture) pics.get(i);
            pic.writeImageContent(new FileOutputStream(new File(picPath + pic.suggestFullFileName())));
        }
    }
    
    Document htmlDocument = wordToHtmlConverter.getDocument();
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    DOMSource domSource = new DOMSource(htmlDocument);
    StreamResult streamResult = new StreamResult(out);
    TransformerFactory tf = TransformerFactory.newInstance();
    Transformer serializer = tf.newTransformer();
    serializer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
    serializer.setOutputProperty(OutputKeys.INDENT, "yes");
    serializer.setOutputProperty(OutputKeys.METHOD, "html");
    serializer.transform(domSource, streamResult);
    String result = new String(out.toByteArray());
    result = result.replaceAll("\\r\\n","
    "
    ); out.close(); System.out.println(result);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    1. 在处理docx的时候,需要先将ips转成file,再转成FileInputStream,否则会报错的。
    file = inputstreamToFile(ips, fileName);
    XWPFDocument document = new XWPFDocument(new FileInputStream(file));
    
    • 1
    • 2

    这里也把inputStream转成File的方法也贴出来了。

    /**
     1. 将inputStream转成File
     2. @param input 文件的输入流
     3. @param fileName 文件名
     4. @return file
     */
    private File inputstreamToFile(InputStream input, String fileName){
        File file = new File("文件保存路径");
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = input.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            input.close();
            return file;
        }catch (Exception e){
            log.error("转换出现错误:",e);
        }
        return file;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    1. FileUtil.del(file)这里主要是处理docx的时候转换出来的file,将其删除。
      该工具类是调用hutool的东西。可引入依赖
    <dependency>
        <groupId>cn.hutoolgroupId>
        <artifactId>hutool-allartifactId>
        <version>5.8.4version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    最后

    目前来说,只是验证了doc/docx文件中存在图片的情况是能够正常生成html字符串的,以及文字的一些样式也能够还原,但是具体像表格这些就没有去验证了。
    至于后续功能上线后,遇到的实际问题和解决方法也会在这篇文章进行更新,大伙可以关注收藏一下。

  • 相关阅读:
    ps常用操作
    数据结构——栈&队列
    【AUTOSAR-CanSM】-2.4-参数CanSMBorTimeTxEnsured详解
    WinRAR CVE-2023-40477代码执行漏洞复现
    dubbo 搞懂配置中心、元数据中心
    this理解不到位,导致十一点半还在改bug
    MySQL定时删除XX天数据
    丹磺酰荧光素/5-羧基四甲基罗丹明标记PLGA纳米载体,Dansyl-PLGA,TMR-PLGA,齐岳
    servlet
    带头节点的单链表练习(写加注释花了5小时,已废)
  • 原文地址:https://blog.csdn.net/haroroc/article/details/125906617