一、程序功能说明
支持上传xls、xlsx格式文档,转换为MarkDown格式文本返回
二、程序依赖包
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
三、接口入口方法controller层代码
/**
* Excel文件转markdown格式文本
* @param file
* @return
* @throws IOException
*/
@RequestMapping(value = "/excelFileToMDtext", method = RequestMethod.POST, produces = "application/json; text/plain; */*")
public String excelFileToMDtext(@RequestParam("file") MultipartFile file) throws IOException {
log.info("收到excel2md请求:file={}", file.getOriginalFilename());
String result = ExcelToMarkdownConverter.convertExcelToMD(file);
log.info("完成excel2md请求:file={}", result);
return result;
}
四、转换工具类ExcelToMarkdownConverter.java
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.openxml4j.exceptions.OfficeXmlFileException;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* Excel 转 Markdown 表格转换器
* 将上传的 Excel 文件(.xls 或 .xlsx)转换为 Markdown 格式的表格字符串
*/
public class ExcelToMarkdownConverter {
/**
* 将 MultipartFile 格式的 Excel 文件转换为 Markdown 表格字符串
*
* @param multipartFile 上传的 Excel 文件
* @return 转换后的 Markdown 字符串
* @throws IOException 文件读取异常
*/
public static String convertExcelToMD(MultipartFile multipartFile) throws IOException {
// 创建临时文件,用于保存上传的 Excel 文件
// 文件名前缀为 "temp",后缀使用原始文件名以保留扩展名
File tempFile = File.createTempFile("temp", multipartFile.getOriginalFilename());
// 标记临时文件在 JVM 退出时自动删除
tempFile.deleteOnExit();
// 将 MultipartFile 内容写入临时文件
multipartFile.transferTo(tempFile);
// 根据文件路径创建 Workbook 实例(兼容 .xls 和 .xlsx)
Workbook workbook = getWorkbookByFilePath(tempFile.getAbsolutePath());
// 获取第一个工作表
Sheet sheet = workbook.getSheetAt(0);
// 使用 StringBuilder 构建 Markdown 字符串
StringBuilder mdBuilder = new StringBuilder();
// 添加标题:## 工作表名称
mdBuilder.append("## ").append(sheet.getSheetName()).append("\n");
// 获取表头行(第一行)
Row headerRow = sheet.getRow(0);
if (headerRow == null) {
throw new IllegalArgumentException("Excel 文件中没有表头(第一行为空)");
}
// 构建表头 Markdown 行
mdBuilder.append("| ");
for (int j = 0; j < headerRow.getLastCellNum(); j++) {
Cell cell = headerRow.getCell(j);
mdBuilder.append(getCellValue(cell)).append(" | ");
}
mdBuilder.append("\n");
// 添加 Markdown 表格分隔行:|---|---|...
mdBuilder.append("|");
for (int i = 0; i < headerRow.getLastCellNum(); i++) {
mdBuilder.append("---|");
}
mdBuilder.append("\n");
// 遍历数据行(从第二行开始)
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
Row row = sheet.getRow(i);
if (row == null) continue; // 跳过空行
// 特殊处理最后一行:如果整行为空(值为 "NaN"),则跳过不输出
if (i == sheet.getLastRowNum()) {
boolean isBlankRow = true;
for (int j = 0; j < row.getLastCellNum(); j++) {
Cell cell = row.getCell(j);
if (!"NaN".equals(getCellValue(cell))) {
isBlankRow = false;
break;
}
}
if (isBlankRow) {
break; // 跳出循环,不处理该空行
}
}
// 开始构建数据行
mdBuilder.append("| ");
for (int j = 0; j < row.getLastCellNum(); j++) {
Cell cell = row.getCell(j);
mdBuilder.append(getCellValue(cell)).append(" | ");
}
mdBuilder.append("\n");
}
// 关闭 Workbook 资源(建议使用 try-with-resources,此处依赖外部调用者管理)
workbook.close();
return mdBuilder.toString();
}
/**
* 获取单元格的字符串值,处理各种数据类型和特殊字符
* 支持公式、数值、字符串、错误类型等
*
* @param cell Excel 单元格
* @return 单元格的格式化字符串值,若为空则返回 "NaN"
*/
private static String getCellValue(Cell cell) {
if (cell == null) {
return "NaN";
}
DataFormatter formatter = new DataFormatter();
String value;
// 处理公式单元格
if (cell.getCellType() == CellType.FORMULA) {
CellType formulaResultType = cell.getCachedFormulaResultType();
switch (formulaResultType) {
case STRING:
value = cell.getStringCellValue();
break;
case NUMERIC:
double numericValue = cell.getNumericCellValue();
value = String.valueOf(numericValue);
break;
case BOOLEAN:
value = String.valueOf(cell.getBooleanCellValue());
break;
case ERROR:
value = ""; // 公式错误时返回空字符串
break;
default:
value = formatter.formatCellValue(cell);
break;
}
} else {
// 非公式单元格,直接格式化
value = formatter.formatCellValue(cell);
}
// 特殊处理数值类型:若为整数,则去除小数点(如 123.0 -> 123)
if (cell.getCellType() == CellType.NUMERIC && !DateUtil.isCellDateFormatted(cell)) {
double num = cell.getNumericCellValue();
if (num == (long) num) {
value = String.valueOf((long) num);
}
}
// 转义 Markdown 特殊字符
value = value.replace("|", "\\|") // 转义竖线,防止破坏表格结构
.replace("\n", " ") // 换行符替换为空格
.trim(); // 去除首尾空格
// 若处理后为空字符串,返回 "NaN" 表示空值
if (value.isEmpty()) {
return "NaN";
}
return value;
}
/**
* 根据文件路径创建 Workbook 实例,兼容 .xls 和 .xlsx 格式
* 支持文件后缀与实际内容不一致的情况(如 .xls 文件实际为 .xlsx 格式)
*
* @param excelFilePath Excel 文件路径
* @return Workbook 实例
* @throws RuntimeException 包装的 IO 异常或文件格式异常
*/
public static Workbook getWorkbookByFilePath(String excelFilePath) {
boolean forceOpenXlsx = false; // 标记是否需要强制以 XSSF 方式打开
Workbook workbook = null;
try (FileInputStream inputStream = new FileInputStream(excelFilePath)) {
if (excelFilePath.toLowerCase().endsWith(".xlsx")) {
workbook = new XSSFWorkbook(inputStream); // 读取 .xlsx 文件
} else if (excelFilePath.toLowerCase().endsWith(".xls")) {
workbook = new HSSFWorkbook(inputStream); // 读取 .xls 文件
} else {
throw new IllegalArgumentException("不支持的文件格式: " + excelFilePath);
}
} catch (IOException e) {
// 文件读取失败
throw new RuntimeException("IO 错误: 无法读取文件 " + excelFilePath, e);
} catch (OfficeXmlFileException e) {
// 文件后缀为 .xls,但内容实际是 .xlsx 格式,需尝试用 XSSF 打开
forceOpenXlsx = true;
}
// 如果检测到 OfficeXmlFileException,尝试以 .xlsx 方式强制打开
if (forceOpenXlsx) {
try (FileInputStream inputStream = new FileInputStream(excelFilePath)) {
workbook = new XSSFWorkbook(inputStream); // 强制使用 XSSF 解析
} catch (IOException e) {
throw new RuntimeException("IO 错误: 无法以 .xlsx 格式读取文件 " + excelFilePath, e);
}
}
return workbook;
}
}

761

被折叠的 条评论
为什么被折叠?



