如何使用Java将word解析出来(包含格式和图片)
作者:未婚男子王某
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并保存到第三方接口中代码逻辑
/** * 解析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查询取该篇文档的标题内容树形结构
/** * 根据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解析出来的文章就介绍到这了,更多相关Java解析word内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!