需求
需求这部分我就不发牢骚了,简单来说就是某些人因为看到别家的项目,觉得人家通过上传试卷的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文档的转换报错,目前问题已经解决,见该部分后面.)
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159package 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(); } } }
- 第一个 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上的一个例子之后得到了解决,这里贴一下代码顺便说一下解决过程.
首先是实现代码,与前面的类似,只是类不同而已,方法和过程都大概差不多
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
33public 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(); } }
接下来就是问题,之前导包导的是这个
1
2
3
4
5
6<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 这里,将依赖换成了
1
2
3
4
5
6<dependency> <groupId>fr.opensagres.xdocreport</groupId> <artifactId>fr.opensagres.poi.xwpf.converter.xhtml</artifactId> <version>2.0.1</version> </dependency>
再次运行,未报错,且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
最后
以上就是玩命钢笔最近收集整理的关于使用POI处理Word为HTML并将HTML中数据整合为所需要的对象形式的全部内容,更多相关使用POI处理Word为HTML并将HTML中数据整合为所需要内容请搜索靠谱客的其他文章。
发表评论 取消回复