Java 实现将Word 转换成markdown

news/2025/4/1 13:27:38/

日常的开发中,需要将word 等各类文章信息转换成格式化语言,因此需要使用各类语言将word 转换成Markdown

1、引入 jar包

  <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency>

2、使用以下代码进行编写

java">package com.nd.ai.test.service.utils;import com.alibaba.fastjson.JSONObject;
import com.nd.ai.test.service.dto.apitest.FileMarkdownDTO;
import org.apache.commons.compress.utils.Lists;
import org.apache.poi.xwpf.usermodel.*;import java.io.*;
import java.util.List;
import java.util.UUID;/*** @ClassName WordToMarkdownConverter* @Author Administrator*/
public class WordToMarkdownConverter {public static FileMarkdownDTO convertWordToMarkdown(String wordPath) throws IOException {List<String> imagePathList = Lists.newArrayList();FileMarkdownDTO dto = new FileMarkdownDTO();try  {File file = new File(wordPath);String uuid = UUID.randomUUID().toString();String outputFilePath = file.getParent() + File.separator + uuid + ".md";File outputFile = new File(outputFilePath);FileInputStream fis = new FileInputStream(file); // Corrected to read the input fileXWPFDocument document = new XWPFDocument(fis);FileWriter writer = new FileWriter(outputFile);dto.setMdPath(outputFilePath);String imageDir = file.getParent() + File.separator + uuid + "-images";new File(imageDir).mkdirs(); // Create image directoryfor (IBodyElement element : document.getBodyElements()) {if (element instanceof XWPFParagraph) {processParagraph((XWPFParagraph) element, writer, imageDir, imagePathList);} else if (element instanceof XWPFTable) {processTable((XWPFTable) element, writer, imageDir, imagePathList);}}writer.close();// 增加读取mardkown 文件内容// Read the content of the generated Markdown fileStringBuilder markdownContent = new StringBuilder();try (BufferedReader br = new BufferedReader(new FileReader(outputFile))) {String line;while ((line = br.readLine()) != null) {markdownContent.append(line).append("\n");}}dto.setMarkdownContent(markdownContent.toString());dto.setParserStatus("success");} catch (IOException e){dto.setParserStatus("error");throw new IOException(e);}dto.setFileImagePathList(imagePathList);return dto;}private static void processParagraph(XWPFParagraph paragraph, FileWriter writer, String imageDir,List<String> imageNamePath) throws IOException {String content = processParagraphContent(paragraph, imageDir,imageNamePath);if (content.isEmpty()) return;// 处理标题和列表样式String style = paragraph.getStyle();if (style != null && style.startsWith("Heading")) {int level = Math.min(Character.getNumericValue(style.charAt(7)), 6);StringBuilder heading = new StringBuilder();for (int i = 0; i < level; i++) {heading.append("#");}heading.append(" ").append(content).append("\n\n");writer.write(heading.toString());
//            writer.write("#".repeat(level) + " " + content + "\n\n");} else if (isListParagraph(paragraph)) {String listMark = getListMark(paragraph);writer.write("* " + listMark + " " + content + "\n");} else {writer.write(content + "\n\n");}}private static String getListMark(XWPFParagraph para) {int indentLevel = para.getNumIlvl() != null ? para.getNumIlvl().intValue() : 0;String numFmt = para.getNumFmt(); // 获取列表编号格式// 处理有序列表if ("decimal".equals(numFmt) || "upperRoman".equals(numFmt)) {StringBuilder prefixBuilder = new StringBuilder();for (int i = 0; i < indentLevel * 4; i++) {prefixBuilder.append(" ");}String prefix = prefixBuilder.toString();return prefix + ".";}// 处理无序列表else {String bullet;switch (para.getNumFmt()) {case "bullet":bullet = "•"; // 实心圆点break;default:bullet = "-"; // 默认用减号break;}StringBuilder prefixBuilder = new StringBuilder();for (int i = 0; i < indentLevel * 4; i++) {prefixBuilder.append(" ");}return prefixBuilder.toString() + bullet;}}private static boolean isListParagraph(XWPFParagraph paragraph) {return isOrderedList(paragraph) || isUnorderedList(paragraph); // 如果没有找到对应的样式,则不可能是列表段落}private static boolean isOrderedList(XWPFParagraph paragraph) {if (paragraph.getNumFmt() != null) {String numFmt = paragraph.getNumFmt();return "decimal".equals(numFmt) || "upperRoman".equals(numFmt) || "lowerRoman".equals(numFmt) ||"upperLetter".equals(numFmt) || "lowerLetter".equals(numFmt);}return false;}private static boolean isUnorderedList(XWPFParagraph paragraph) {if (paragraph.getNumFmt() != null) {String numFmt = paragraph.getNumFmt();return "bullet".equals(numFmt);}return false;}private static void processTable(XWPFTable table, FileWriter writer, String imageDir,List<String> imageNamePath) throws IOException {StringBuilder mdTable = new StringBuilder();List<XWPFTableRow> rows = table.getRows();for (int i = 0; i < rows.size(); i++) {XWPFTableRow row = rows.get(i);mdTable.append("|");// 处理每个单元格for (XWPFTableCell cell : row.getTableCells()) {StringBuilder cellContent = new StringBuilder();// 处理单元格内的段落for (XWPFParagraph para : cell.getParagraphs()) {cellContent.append(processParagraphContent(para, imageDir,imageNamePath).replace("\n", "<br>"));}mdTable.append(cellContent.toString().trim()).append("|");}mdTable.append("\n");// 添加表头分隔线if (i == 0) {mdTable.append("|");for (int j = 0; j < row.getTableCells().size(); j++) {mdTable.append(" --- |");}mdTable.append("\n");}}writer.write(mdTable + "\n\n");}private static String processParagraphContent(XWPFParagraph paragraph, String imageDir,List<String> imageNamePath) throws IOException {StringBuilder sb = new StringBuilder();for (XWPFRun run : paragraph.getRuns()) {// 处理图片for (XWPFPicture picture : run.getEmbeddedPictures()) {sb.append(saveImage(picture, imageDir,imageNamePath)).append(" ");}// 处理文本样式String text = run.getText(0);if (text == null) continue;text = applyTextStyles(run, text);sb.append(text);}String content = sb.toString().trim();// 处理有序列表和无序列表if (isListParagraph(paragraph)) {String listMark = getListMark(paragraph);content ="* " + listMark + " " + content;}return content;}private static String applyTextStyles(XWPFRun run, String text) {if (run.isBold()) text = "**" + text + "**";if (run.isItalic()) text = "*" + text + "*";if (run.getUnderline() != UnderlinePatterns.NONE) text = "__" + text + "__";return text;}private static String saveImage(XWPFPicture picture, String imageDir,List<String> imageNamePath) throws IOException {XWPFPictureData picData = picture.getPictureData();String fileName = "img_" + UUID.randomUUID() + "." + picData.suggestFileExtension();File output = new File(imageDir, fileName);imageNamePath.add(output.getPath());try (FileOutputStream fos = new FileOutputStream(output)) {fos.write(picData.getData());}return "![" + fileName + "](" + imageDir + "/" + fileName + ")";}public static void main(String[] args) throws Exception {System.out.println(JSONObject.toJSONString( convertWordToMarkdown("word path")));}
}

获得信息

java">{
"fileImagePathList":["文件中图片路径1","文件中图片路径2"],
"markdownContent": "markdwon 信息",
"mdPath": "markdown文件地址"
}

运行上方的程序将会得到
1、解析文件中所有图片信息,保存到下方的地址
2、将word 文档转换成markdown
3、获取markdown 文件


http://www.ppmy.cn/news/1584209.html

相关文章

如何在 Postman 中正确设置 Session 以维持用户状态?

在 Postman 里面设置有 session 的请求。如果你还不知道什么是 session&#xff0c;那么请看这里—— session 是一种记录客户端和服务器之间状态的机制&#xff0c;用于保持用户的登录状态或者其他数据&#xff0c;从而让用户在不同页面之间保持一致的体验。 Postman 设置带 …

谈谈空间复杂度考量,特别是递归调用栈空间消耗?

空间复杂度考量是算法设计的核心要素之一&#xff0c;递归调用栈的消耗问题在前端领域尤为突出。 以下结合真实开发场景进行深度解析&#xff1a; 一、递归调用栈的典型问题 1. 深层次DOM遍历的陷阱 // 危险操作&#xff1a;递归遍历未知层级的DOM树 function countDOMNode…

vue3中watch 函数参数说明

source&#xff1a;这是要监听的数据源&#xff0c;可以是一个 getter 函数、一个 ref 对象、一个 reactive 对象或者一个包含多个数据源的数组。在子组件示例中&#xff0c;() > props.parentValue 就是一个 getter 函数&#xff0c;它返回 props.parentValue 的值&#xf…

游戏引擎学习第187天

看起来观众解决了上次的bug 昨天遇到了一个相对困难的bug&#xff0c;可以说它相当棘手。刚开始的时候&#xff0c;没有立刻想到什么合适的解决办法&#xff0c;所以今天得从头开始&#xff0c;逐步验证之前的假设&#xff0c;收集足够的信息&#xff0c;逐一排查可能的原因&a…

211 本硕研三,已拿 C++ 桌面应用研发 offer,计划转音视频或嵌入式如何规划学习路线?

今天给大家分享的是一位粉丝的提问&#xff0c;211 本硕研三&#xff0c;已拿 C 桌面应用研发 offer&#xff0c;计划转音视频或嵌入式如何规划学习路线&#xff1f; 接下来把粉丝的具体提问和我的回复分享给大家&#xff0c;希望也能给一些类似情况的小伙伴一些启发和帮助。 …

清华大学.智灵动力-《DeepSeek行业应用实践报告》附PPT下载方法

导 读INTRODUCTION 今天分享是由清华大学.智灵动力&#xff1a;《DeepSeek行业应用实践报告》&#xff0c;主要介绍了DeepSeek模型的概述、优势、使用技巧、与其他模型的对比&#xff0c;以及在多个行业中的应用和未来发展趋势。为理解DeepSeek模型的应用和未来发展提供了深入的…

Java 中装饰者模式与策略模式在埋点系统中的应用

前言 在软件开发中&#xff0c;装饰者模式和策略模式是两种常用的设计模式&#xff0c;它们在特定的业务场景下能够发挥巨大的作用。本文将通过一个实际的埋点系统案例&#xff0c;探讨如何在 Java 中运用装饰者模式和策略模式&#xff0c;以及如何结合工厂方法模式来优化代码…

OpenCV图像拼接(10)用于实现图像拼接过程中的时间流逝(timelapse)效果的一个类cv::detail::Timelapser

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::detail::Timelapser 是 OpenCV 库中用于实现图像拼接过程中的时间流逝&#xff08;timelapse&#xff09;效果的一个类。它通常用于将一系列…