1、需求:
a. 将word中的内容按照层级结构解析出来
b. 不区分文件的后缀
c. 包含word的样式
2、思路:总体思路分为存和取,存的是文档的标题和内容、图片等;取的是文档的树形结构。
(1). 存:将word中的标题、内容、图片获取出来并进行存储
a. 上传文档时,获取到文档的名称,存储到数据库表中,产生一个id,即documentId
b. 解析word之后,按照顺序遍历获取每一个标题进行存储,父标题和子标题之间使用parentId进行关联,即子标题中字段parentId是父标题的id
c. 在所有的标题中都添加一个documentId方便后期范围查询
d. 根节点即一级标题的父id为0
e. 查到的内容如果是p标签,则与自己的上一级进行关联,即p标签的父id为它的上一级标签id
f. 关联完成之后,生成一个树形结构,并遍历存储到第三方平台的数据库中。
(2). 取:根据documentId查询该文档的树形结构并返回
a. 根据documentId查询该文档的所有标题并存储到集合中去
b. 对集合进行遍历,为了充分使用递归,规定最顶层的标题的父节点为0,从父节点为0的元素开始递归,并将产生的结果生成树形结构并返回。
3、注意:
(1). 因为自己做的主要是在第三方平台上的操作,所以类似于DmeTestRequestUtil.getDmeResult() 这样的方法主要是跟第三方平台的交互,出于安全性考虑就不放出来了。
(2). 包含word的样式:要思考的时怎么才能够获得word的样式,即获得html文件
(3). 在解析的过程中样式是以行内式还是其他的效果呈现:可以转换为行内式
(4). 为什么不用poi:使用poi最恶心的是要考虑word的版本问题
(5). 使用的依赖主要是什么:aspose、jsoup
(6). 层级结构解析主要是判断h、p的顺序关系
4、代码:
(1). 依赖
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.2</version>
</dependency>
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>15.8.0</version>
</dependency>
(2). 解析word并保存到第三方接口中代码逻辑文章来源:https://www.toymoban.com/news/detail-738777.html
/**
* 解析word
* @param multipartFile 前端接收的文件,根据自己的需求也可以将MultipartFile转换为File
* @return TitleTreeVO 存放标题的实体
* @author WangKuo
* @date 2023/7/27 11:01
*/
public TitleTreeVO wordAnalysis(MultipartFile multipartFile) throws IOException {
byte[] byteArr = multipartFile.getBytes();
InputStream inputStream = new ByteArrayInputStream(byteArr);
List<DocumentContentVO> documentContentVOList = new LinkedList<>();
TitleTreeVO titleTreeVO = new TitleTreeVO();
try {
// 把流转化为Document
com.aspose.words.Document doc = new com.aspose.words.Document(inputStream);
// 设置转化的格式,HtmlSaveOptions转换为HTML格式
HtmlSaveOptions saveOptions = new HtmlSaveOptions();
saveOptions.setExportImagesAsBase64(false);
// 将所有word中的图片放在临时文件夹中,并将html中的链接替换为临时文件夹中绝对路径
String property = System.getProperty("java.io.tmpdir");
saveOptions.setImagesFolder(property);
org.apache.commons.io.output.ByteArrayOutputStream baos = new ByteArrayOutputStream();
doc.save(baos, saveOptions);
String token = DmeTestRequestUtil.getToken();
// 将html文件转化为Document,方便后续使用jsoup的操作
org.jsoup.nodes.Document htmlDoc = Jsoup.parse(baos.toString());
// 设置html里面的图片src路径
this.setImagePath(htmlDoc, token);
// 存储word文档的名称
String substring = multipartFile.getOriginalFilename().substring(0, multipartFile.getOriginalFilename().lastIndexOf("."));
JSONObject docParam = this.getDocParam(substring);
String saveDocUrl = "https://dme.cn-south-4.huaweicloud.com/rdm_hwdmeverify_app/publicservices/api/DocumentSave/create";
// 首先根据文档名称生成一条document的数据,产生的id将在标题实体中进行关联
String dmeResult = DmeTestRequestUtil.getDmeResult(saveDocUrl, docParam, token);
JSONObject jsonObject1 = JSONObject.parseObject(dmeResult);
List data1 = jsonObject1.getObject("data", List.class);
JSONObject jsonObjectData1 = (JSONObject) data1.get(0);
String id = jsonObjectData1.getString("id");//文档id
// 存储文档的第一个标题的返回结果,其中包含该节点的id和title
documentContentVOList = this.exactContentFromHtml(htmlDoc);
this.dmeSave(documentContentVOList, id, "0", token);//第一个标题的父ID默认为0
} catch (Exception e) {
e.printStackTrace();
} finally {
inputStream.close();
}
return titleTreeVO;
}
/**
* 在解析为html文件的时候需要将图片的地址进行一个替换,由最初的临时文件地址替换为图片在服务器上的位置
* 设置图片的路径(src)
* @param document 转换为HTML的文档内容
* @return void
* @author Wangkuo
* @date 2023/7/25 21:40
*/
private void setImagePath(Document document) throws IOException {
Elements imgs = document.select("img");
String token = DmeTestRequestUtil.getToken();
for (Element img : imgs) {
// 获取出html中src内的地址值
String src = img.attr("src");
// 通过地址查到对应的文件
File file = new File(src);
FileInputStream input = new FileInputStream(file);
// 将file转化为MultipartFile
MultipartFile multipartFile =new MockMultipartFile("file", file.getName(), "text/plain", IOUtils.toByteArray(input));
// 该部分主要是第三方接口设置的必须传的参数,在这里我就先设置为定值,因为这些不干扰我的需求结果
FormVo formVo = new FormVo();
formVo.setAttributeName("File");
formVo.setModelName("Document");
formVo.setApplicationId("-1");
String uploadImgUrl = "图片作为文件,进行上传";
String uploadImage = DmeTestRequestUtil.getDmeResultUploadFile(uploadImgUrl, multipartFile, formVo, token);
JSONObject uploadImgJs = JSONObject.parseObject(uploadImage);
List data = uploadImgJs.getObject("data", List.class);
// 上传完成后,第三方接口会返回一个文件的id,我可以根据这个id进行文件的预览和下载
String id = (String) data.get(0);
//上传文件file 并返回上传的路径;将路径拼接出来,并替换到html的document中
String imgPath = "/api/dme-library/LibraryFolder/preview?fileId="+id;
img.attr("src",imgPath);
input.close();
// 删除临时文件夹中存储的文件
file.deleteOnExit();
}
}
/**
* 该部分主要是第三方接口在调用时规定该接口的参数格式
* 拼接参数
* @param name
* @return com.alibaba.fastjson.JSONObject
* @author Wangkuo
* @date 2023/7/27 11:23
*/
private JSONObject getDocParam(String name) {
Map<String, Object> mapStr = new HashMap<>();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("name", name);
mapStr.put("params", paramMap);
JSONObject jsonObject = new JSONObject(mapStr);
return jsonObject;
}
/**
* 处理树形结构
* @param htmlDoc
* @return java.util.List<DocumentContentVO>
* @author Wangkuo
* @date 2023/7/27 11:26
*/
private List<DocumentContentVO> exactContentFromHtml(Document htmlDoc) throws Exception {
Elements eleList = htmlDoc.getElementsByTag("h1");
if (eleList == null || eleList.size() == 0) {
throw new Exception("上传的文件中不存在一级标题,请检查!");
}
Element hElement = htmlDoc.selectFirst("h1");//从第一个标题1 开始往下找
//Elements pElement = htmlDoc.select("h1");
List<DocumentContentVO> allTreeList = new ArrayList<>();
List<DocumentContentVO> list2 = new ArrayList<>();
List<DocumentContentVO> list3 = new ArrayList<>();
List<DocumentContentVO> list4 = new ArrayList<>();
DocumentContentVO b1Map = new DocumentContentVO();
DocumentContentVO b2Map = new DocumentContentVO();
DocumentContentVO b3Map = new DocumentContentVO();
DocumentContentVO b4Map = new DocumentContentVO();
DocumentContentVO bMap = b1Map;//记录当前map
//先将第一个标题 放入
int i = 1;
b1Map.setTitle(hElement.toString());
b1Map.setIndex(i);
allTreeList.add(b1Map);
while (hElement.nextElementSibling() != null) { //如果存在下一个标题
i++;
hElement = hElement.nextElementSibling();
String nodeName = hElement.nodeName();
String s = hElement.tagName();
//System.out.println(s);
if (Objects.equals(nodeName, "h1")) {
b1Map = new DocumentContentVO();
bMap = b1Map;
b1Map.setTitle(hElement.toString());
b1Map.setIndex(i);
allTreeList.add(b1Map);
list2 = new ArrayList<>();
} else if (Objects.equals(nodeName, "h2")) {
b2Map = new DocumentContentVO();
bMap = b2Map;
list3 = new ArrayList<>();
b2Map.setTitle(hElement.toString());
b2Map.setIndex(i);
list2.add(b2Map);
b1Map.setChildList(list2);
} else if (Objects.equals(nodeName, "h3")) {
b3Map = new DocumentContentVO();
bMap = b3Map;
b3Map.setTitle(hElement.toString());
b3Map.setIndex(i);
list3.add(b3Map);
b2Map.setChildList(list3);
} else if (Objects.equals(nodeName, "h4")) {
b4Map = new DocumentContentVO();
bMap = b4Map;
b4Map.setTitle(hElement.toString());
b4Map.setIndex(i);
list4.add(b4Map);
b3Map.setChildList(list4);
} else {
bMap.setContent(bMap.getContent() == null ? hElement.toString() : bMap.getContent() + hElement.toString());
}
}
return allTreeList;
}
/**
* 传入html解析的树 和对应文档id 通过递归实现保存
*
* @param treeList
* @param id
* @param parentId
* @return java.lang.String
* @author Wangkuo
* @date 2023/7/25 16:48
*/
private String dmeSave(List<DocumentContentVO> treeList, String id, String parentId,String token) {
String dmeResult = null;
for (DocumentContentVO documentContentVO : treeList) {
if (documentContentVO != null) {
String title = documentContentVO.getTitle();
int sort = documentContentVO.getIndex();
String content = documentContentVO.getContent();
String url = "创建对应数据的第三方url";
JSONObject jsonObjectParam = this.paramJoint1(title, id, parentId, sort, content);
dmeResult = DmeTestRequestUtil.getDmeResult(url, jsonObjectParam, token);
List data = JSONObject.parseObject(dmeResult).getObject("data", List.class);
if (data != null && !data.isEmpty()) {
JSONObject jsonObject = (JSONObject) data.get(0);
String parentIdNext = jsonObject.getString("id");
if (documentContentVO.getChildList() != null && documentContentVO.getChildList().size() > 0) {
dmeSave(documentContentVO.getChildList(), id, parentIdNext,token);
}
}
}
}
return dmeResult;
}
/**
* 同理,第三方接口规定的参数样式,需要进行拼接
* @param title
* @param id
* @param parentId
* @param sort
* @param content
* @return com.alibaba.fastjson.JSONObject
* @author Wangkuo
* @date 2023/7/27 11:30
*/
private JSONObject paramJoint1(String title, String id, String parentId, int sort, String content) {
Map<String, Object> mapStr = new HashMap<>();
Map<String, Object> paramsMap = new HashMap<>();
if (id != null) {
paramsMap.put("title", title);
paramsMap.put("sort", sort);
paramsMap.put("parentId", parentId);
paramsMap.put("content", content);
paramsMap.put("documentId", id);
} else {
paramsMap.put("title", title);
}
mapStr.put("params", paramsMap);
return JSONObject.parseObject(JSON.toJSONString(mapStr));
}
(3). 根据documentId查询取该篇文档的标题内容树形结构文章来源地址https://www.toymoban.com/news/detail-738777.html
/**
* 根据documentId查询对应的word文档的树形结构
* @param reqJSON {"id":"525663360008593408"}
* @return java.util.List<TitleTreeVO>
* @author Wangkuo
* @date 2023/7/27 11:32
*/
public List<TitleTreeVO> getTreeCon(JSONObject reqJSON) {
String id = reqJSON.getString("id");
List<TitleTreeVO> allTitleByDocId = this.getAllTitleByDocId(id);
TitleTreeVO titleTreeVO = new TitleTreeVO();
titleTreeVO.setId("0");
this.getChild(titleTreeVO,allTitleByDocId);
return titleTreeVO.getChildList();
}
/**
* 根据文档id获取到该文档的所有标题(此时获取的集合没有父子级关系)
* @param docId
* @return java.util.List<TitleTreeVO>
* @author Wangkuo
* @date 2023/7/27 11:34
*/
private List<TitleTreeVO> getAllTitleByDocId(String docId) {
String url = "第三方标题表的查询";
// 参数拼接
JSONObject docIdParam = getDocIdParam(docId);
String token = DmeTestRequestUtil.getToken();
String dmeResult = DmeTestRequestUtil.getDmeResult(url, docIdParam, token);
JSONObject jsonObject = JSONObject.parseObject(dmeResult);
List data = jsonObject.getObject("data", List.class);
List<TitleTreeVO> titleList = new ArrayList<>();
if (data != null && !data.isEmpty()) {
for (Object title : data) {
JSONObject titleJson = (JSONObject)title;
TitleTreeVO titleTreeVO = new TitleTreeVO();
titleTreeVO.setContent(titleJson.getString("content"));
titleTreeVO.setTitle(titleJson.getString("title"));
titleTreeVO.setId(titleJson.getString("id"));
titleTreeVO.setIndex(Integer.parseInt(titleJson.getString("sort")));
titleTreeVO.setDocumentId(titleJson.getString("documentId"));
titleTreeVO.setParentId(titleJson.getString("parentId"));
titleList.add(titleTreeVO);
}
}
return titleList;
}
/**
* 通过递归获取到各级的子标题和内容
* @param parentTitleTreeVO
* @param titleListOld
* @return TitleTreeVO
* @author Wangkuo
* @date 2023/7/27 11:42
*/
private TitleTreeVO getChild(TitleTreeVO parentTitleTreeVO,List<TitleTreeVO> titleListOld) {
List<TitleTreeVO> titleList = new ArrayList<>();
if (titleListOld != null && titleListOld.size()>0) {
List<TitleTreeVO> titleCollect = titleListOld.stream().filter(e -> e.getParentId().equals(parentTitleTreeVO.getId())).collect(Collectors.toList());
if(titleCollect.size()>0){
for (TitleTreeVO title : titleCollect) {
TitleTreeVO titleTreeVO = new TitleTreeVO();
titleTreeVO.setIndex(title.getIndex());
titleTreeVO.setTitle(title.getTitle());
titleTreeVO.setId(title.getId());
titleTreeVO.setContent(title.getContent());
titleTreeVO.setDocumentId(title.getDocumentId());
titleTreeVO.setParentId(title.getParentId());
titleList.add(titleTreeVO);
this.getChild(titleTreeVO,titleListOld);
}
}
}
List<TitleTreeVO> titleSortList = titleList.stream().sorted(Comparator.comparing(TitleTreeVO::getIndex)).collect(Collectors.toList());
parentTitleTreeVO.setChildList(titleSortList);
return parentTitleTreeVO;
}
到了这里,关于使用Java将word解析出来,包含格式和图片的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!