使用POI处理Word为HTML并将HTML中数据整合为所需要的对象形式

需求

需求这部分我就不发牢骚了,简单来说就是某些人因为看到别家的项目,觉得人家通过上传试卷的Word文档直接实现了录题功能眼馋了,想要我们也做一个.

现状

目前项目使用的是手工录题,一道一道的,比较繁琐,所以有些人就想要搬别人的东西过来.
通过几天的思考和查阅资料,知道POI有可以对Word处理的类和方法,但是呢,由于项目目前是通过富文本编辑器来录题,录过之后存入数据库的样式带有HTML标签的内容,在根据题目生成试卷的时候也是需要从标签中获取样式的.所以直接对Word处理是不现实的,于是考虑将Word转为HTML文件,再通过Jsoup中的方法对标签进行处理

设计思路

简单来说就是这个过程:
Word文档——HTML文件——Jsoup获取p标签——对标签内容进行处理
使用的类分别为
HWPFDocument获取Word文件
WordToHtmlConverter类实现转HTML
Jsoup的getElementsByTag获取HTML中的p标签

实现过程

97-2003版本Word(.doc)
对于Word2003(.doc)类型的文档的处理,如果导入文档报错,可将文档用Office打开,并另存为Word97-2003文档,即可使用.
(之前2007版本以上的.docx文档的转换报错,目前问题已经解决,见该部分后面.)
在这里插入图片描述

package com.education.modules.tool.oss.utils;


import com.education.common.utils.Util;
import com.education.modules.questions.entity.QuestionsEntity;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.xwpf.usermodel.IBodyElement;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFStyles;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import javax.lang.model.util.Elements;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class WordUtil {

    public static void getDoc(){
        //解析Word文档,暂时没有用了,用下面那个!
        String path="D:\\testWord\\2019年江苏省南京市高一上学期期末考试_9099.docx";
        InputStream is=null;
        {
            try {
                is = new FileInputStream(path);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        XWPFDocument doc=null;

        {
            try {
                doc = new XWPFDocument(is);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        List<XWPFParagraph> paragraph=doc.getParagraphs();
        List<IBodyElement> bodyElements=doc.getBodyElements();
        XWPFStyles styles=doc.getStyles();
        System.out.println(paragraph);
        System.out.println(bodyElements);
        System.out.println(styles);
        for(XWPFParagraph p:paragraph){
            System.out.println(p.getParagraphText());
        }
    }

    public static void wordToHtml(){
        //读取Word文档转为HTML文件
        String path="D:\\testWord\\2019年江苏省南京市高一上学期期末考试_90999.doc";
        String imagePath="D:\\testWord\\image\\";
        String htmlPath="D:\\testWord\\testHtml.html";
        File file=new File(path);
        OutputStreamWriter outputStreamWriter = null;
        if(!file.exists()){
            System.out.println("文件不存在");
        }else try {
            //加载word文档生成XWPF对象
            InputStream in = new FileInputStream(file);
            //XWPFDocument document = new XWPFDocument(in);
            HWPFDocument doc=new HWPFDocument(in);
            org.w3c.dom.Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            WordToHtmlConverter wordToHtmlConverter=new WordToHtmlConverter(document);
            //保存图片,并返回图片的相对路径
            wordToHtmlConverter.setPicturesManager((content, pictureType, name, width, height) -> {
                try (FileOutputStream out = new FileOutputStream(imagePath + name)) {
                    out.write(content);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return "image/" + name;
            });
            wordToHtmlConverter.processDocument(doc);
            org.w3c.dom.Document htmlDocument = wordToHtmlConverter.getDocument();
            DOMSource domSource = new DOMSource(htmlDocument);
            StreamResult streamResult = new StreamResult(new File(htmlPath));
            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);

            //XHTML的方法.用不了
            /*XHTMLOptions options = XHTMLOptions.create();
            //图片目录
            options.setExtractor(new FileImageExtractor(new File(imagePath)));
            //html中图片的路径
            options.URIResolver((new BasicURIResolver("image")));
            outputStreamWriter = new OutputStreamWriter(new FileOutputStream(htmlPaht), "utf-8");
            XHTMLConverter xhtmlConverter = (XHTMLConverter) XHTMLConverter.getInstance();
            xhtmlConverter.convert(document, outputStreamWriter, options);*/

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        //getDoc();
        //wordToHtml();
        //读取解析之后的HTML文件,获取p标签并遍历.
        File input = new File("D:\\testWord\\testHtml.html");
        try {
            Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");
            org.jsoup.select.Elements pList=doc.getElementsByTag("p");
            List<QuestionsEntity> questionList=new ArrayList<>();
            int i=1;//题号
            for(int j=0;j<pList.size();j++){
                Element p=pList.get(j);
                if(p.text().contains(i+".")){
                    QuestionsEntity question=new QuestionsEntity();
                    question.setStem(p.toString());
                    questionList.add(question);
                    i++;
                }else if (p.text().contains("一、")||p.text().contains("二、")||p.text().contains("三、")||p.text().contains("四、")
                        ||p.text().contains("五、")||p.text().contains("六、")||p.text().contains("七、")||p.text().contains("八、")
                        ||p.text().contains("九、")||p.text().contains("十、")){
                    continue;
                }else{
                    if(Util.isNotEmpty(questionList)&&questionList.size()>0){
                        QuestionsEntity q=questionList.get(i-2);
                        q.setStem(q.getStem()+p.toString());
                    }
                }

            }
            System.out.println(questionList);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }



    }

  1. 第一个 getDoc()方法,是我尝试解析Word文档时候用的,确实可以解析出文字和图片,但是没有样式,所以考虑了第二种方法.
  2. 第二个方法wordToHTML();是读取一个本地的Word文件,将它解析为HTML文件,并对其中的图片进行处理,返回相对路径.后续如果实际在项目中应用的话,应该会选择上传到云服务器,返回URL的方法.
  3. 最后main()方法里面,是我对HTML的一个处理,将HTML文件中的p标签的集合获取,将这个集合进行遍历,同时定义一个题号变量i,如果当前p标签中包含"题号."的字样,则说明这是一道题,将这个p标签中的内容set进一个新建Question对象中的题干属性.继续遍历,如果这一行不是大题题号什么的,就说明它是上一道题未完的部分,将它拼接进上一道Question对象的题干属性中.
    最后返回一个Question的集合.

后期再项目中,可能考虑一个对象的其他各种各样的属性该如何赋值,例如科目,题型,创建人,知识点等等等等,以及如何通过读取标签,获取题型.

2007版本Word(.docx)
之前查看了对于.docx文件的处理方法,需要用到XWPF包和XHTMLConverter类,但是HTML相关的类导包一直出错,导包解决之后调用又出现错误,在参考了github上的一个例子之后得到了解决,这里贴一下代码顺便说一下解决过程.
首先是实现代码,与前面的类似,只是类不同而已,方法和过程都大概差不多

public static void docxToHtml(){
        String path="D:\\testWord\\2019年江苏省南京市高一上学期期末考试_9099.docx";
        String imagePath="D:\\testWord\\image\\";
        String outPath="D:\\testWord\\testDocx.html";
        File file=new File(path);
        File imageFile=new File(imagePath);
        if(!file.exists()){
            System.out.println("文件不存在!");
        }
        try {
            InputStream in=new FileInputStream(file);
            XWPFDocument document=new XWPFDocument(in);
            //存储图片
            XHTMLOptions options=XHTMLOptions.create().URIResolver(new FileURIResolver(imageFile));
            options.setExtractor(new FileImageExtractor(imageFile));
            OutputStream out=new FileOutputStream(outPath);
            /*Writer writer=new FileWriter(file,true);
            IContentHandlerFactory factory = options.getContentHandlerFactory();
            if (factory == null) {
                factory = DefaultContentHandlerFactory.INSTANCE;
            }
            ContentHandler contentHandler = factory.create(out, writer, options);
            //XHTMLConverter.getInstance().convert(document,out,options);*/
            XHTMLConverter converter=new XHTMLConverter();
            converter.convert(document,out,options);

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

接下来就是问题,之前导包导的是这个

		<dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>org.apache.poi.xwpf.converter.xhtml</artifactId>
            <version>1.0.6</version>
        </dependency>

运行会报错NoSuchMethodError.报错信息如下:
Exception in thread “main” java.lang.NoSuchMethodError: org.apache.poi.POIXMLDocumentPart.getPackageRelationship()Lorg/apache/poi/openxml4j/opc/PackageRelationship;
at org.apache.poi.xwpf.converter.core.styles.XWPFStylesDocument.getFontsDocument(XWPFStylesDocument.java:1479)
at org.apache.poi.xwpf.converter.core.styles.XWPFStylesDocument.(XWPFStylesDocument.java:190)
at org.apache.poi.xwpf.converter.xhtml.internal.styles.CSSStylesDocument.(CSSStylesDocument.java:100)
at org.apache.poi.xwpf.converter.xhtml.internal.XHTMLMapper.createStylesDocument(XHTMLMapper.java:147)
at org.apache.poi.xwpf.converter.core.XWPFDocumentVisitor.(XWPFDocumentVisitor.java:159)
at org.apache.poi.xwpf.converter.xhtml.internal.XHTMLMapper.(XHTMLMapper.java:137)
at org.apache.poi.xwpf.converter.xhtml.XHTMLConverter.convert(XHTMLConverter.java:72)
at org.apache.poi.xwpf.converter.xhtml.XHTMLConverter.doConvert(XHTMLConverter.java:63)
at org.apache.poi.xwpf.converter.xhtml.XHTMLConverter.doConvert(XHTMLConverter.java:38)
at org.apache.poi.xwpf.converter.core.AbstractXWPFConverter.convert(AbstractXWPFConverter.java:45)
at com.education.modules.tool.oss.utils.WordUtilForDOCX.docxToHtml(WordUtilForDOCX.java:47)
at com.education.modules.tool.oss.utils.WordUtilForDOCX.main(WordUtilForDOCX.java:92)

后来参考了https://github.com/opensagres/xdocreport/issues/208 这里,将依赖换成了

		<dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>fr.opensagres.poi.xwpf.converter.xhtml</artifactId>
            <version>2.0.1</version>
        </dependency>

再次运行,未报错,且HTML文件正确生成,完美解决!!!
被旧的教程坑了!!!
特此记录!

局限

  1. 文档格式受限
    在翻阅了很多类似的案例之后,看到Word转HTML基本都是使用POI来实现的,对于2003版本(.doc)使用HWPF,对于2007版本使用XWPF.
    但在实际实现的过程中遇到了一个问题,那就是别人项目中的org.apache.poi.xwpf.converter.xhtml.XHTMLConverter的这个类,找不到对应的Jar包,所以目前只实现了使用HWPF的方法,后续可能会再找相关资料尽量实现出来.

  2. 对象操作不可预期
    目前对于p标签整合为对象的过程过于简单,后续实际操作可能有更加复杂繁琐的属性需要设置,只能到时候遇到再说了.

参考资料:
https://github.com/opensagres/xdocreport/issues/208

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值