使用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 paragraph=doc.getParagraphs();List 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 questionList=new ArrayList<>();int i=1;//题号for(int j=0;j0){QuestionsEntity q=questionList.get(i-2);q.setStem(q.getStem()+p.toString());}}}System.out.println(questionList);} catch (IOException e) {e.printStackTrace();}}}
- 第一个 getDoc()方法,是我尝试解析Word文档时候用的,确实可以解析出文字和图片,但是没有样式,所以考虑了第二种方法.
- 第二个方法wordToHTML();是读取一个本地的Word文件,将它解析为HTML文件,并对其中的图片进行处理,返回相对路径.后续如果实际在项目中应用的话,应该会选择上传到云服务器,返回URL的方法.
- 最后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();}}
接下来就是问题,之前导包导的是这个
fr.opensagres.xdocreport org.apache.poi.xwpf.converter.xhtml 1.0.6
运行会报错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 这里,将依赖换成了
fr.opensagres.xdocreport fr.opensagres.poi.xwpf.converter.xhtml 2.0.1
再次运行,未报错,且HTML文件正确生成,完美解决!!!
被旧的教程坑了!!!
特此记录!
局限
-
文档格式受限
在翻阅了很多类似的案例之后,看到Word转HTML基本都是使用POI来实现的,对于2003版本(.doc)使用HWPF,对于2007版本使用XWPF.
但在实际实现的过程中遇到了一个问题,那就是别人项目中的org.apache.poi.xwpf.converter.xhtml.XHTMLConverter的这个类,找不到对应的Jar包,所以目前只实现了使用HWPF的方法,后续可能会再找相关资料尽量实现出来. -
对象操作不可预期
目前对于p标签整合为对象的过程过于简单,后续实际操作可能有更加复杂繁琐的属性需要设置,只能到时候遇到再说了.
参考资料:
https://github.com/opensagres/xdocreport/issues/208
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
