核心思路
转换过程通常分为以下几个步骤:

- 加载 Excel 文件:使用 Java 库读取 Excel 文件,将其内容加载到内存中。
- 遍历工作表:Excel 文件可以包含多个工作表,通常我们按顺序处理它们。
- 遍历单元格:对每个工作表中的每一行、每一个单元格进行遍历。
- 提取样式和数据:获取单元格中的值(文本、数字、日期等)以及其样式(字体、颜色、背景、边框等)。
- 生成 HTML 标签:根据提取的数据和样式,构建对应的 HTML 标签(如
<table>,<tr>,<td>,<b>,<span style="...">等)。 - 写入输出文件:将生成的 HTML 字符串写入到一个
.html文件中。
使用 Apache POI (推荐)
Apache POI 是 Java 处理 Microsoft Office 格式文件最强大、最流行的开源库,它提供了对 .xls (HSSF) 和 .xlsx (XSSF) 的完整支持。
优点:
- 功能最全面:可以精确控制 Excel 的几乎所有元素,包括样式、公式、图片、图表(需额外库)、合并单元格等。
- 社区活跃:遇到问题容易找到解决方案。
- 行业标准:是 Java Office 操作的事实标准。
缺点:
- API 较为底层:如果只为了转 HTML,需要编写较多代码来手动处理样式和表格结构。
- 依赖较多:核心库本身不大,但引入图片、图表等功能会增加依赖。
实现步骤
-
添加 Maven 依赖
(图片来源网络,侵删)在你的
pom.xml文件中添加以下依赖。poi和poi-ooxml分别用于处理.xls和.xlsx格式。<dependencies> <!-- Apache POI Core --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.5</version> </dependency> <!-- Apache POI for Office Open XML (e.g., .xlsx) --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.5</version> </dependency> <!-- SLF4J API (a logging facade) --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.9</version> </dependency> <!-- Simple implementation of SLF4J --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>2.0.9</version> </dependency> </dependencies> -
编写 Java 代码
下面是一个完整的示例,它将处理样式(包括字体、颜色、边框、对齐方式)并输出结构良好的 HTML。
import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import java.io.*; import java.util.HashMap; java.util.Map; public class ExcelToHtmlConverter { public static void main(String[] args) { // 输入的 Excel 文件路径 String excelFilePath = "input.xlsx"; // 输出的 HTML 文件路径 String htmlFilePath = "output.html"; try { convertExcelToHtml(excelFilePath, htmlFilePath); System.out.println("转换成功!HTML 文件已生成于: " + htmlFilePath); } catch (IOException e) { System.err.println("转换过程中发生错误: " + e.getMessage()); e.printStackTrace(); } } public static void convertExcelToHtml(String excelFilePath, String htmlFilePath) throws IOException { // 1. 打开 Excel 文件 try (InputStream is = new FileInputStream(excelFilePath); Workbook workbook = WorkbookFactory.create(is)) { StringBuilder htmlBuilder = new StringBuilder(); // 2. 构建 HTML 头部 htmlBuilder.append("<!DOCTYPE html>\n<html>\n<head>\n") .append("<meta charset=\"UTF-8\">\n") .append("<title>Excel to HTML</title>\n") .append("<style>\n") // 内联 CSS 样式 .append(" table { border-collapse: collapse; width: 100%; }\n") .append(" th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }\n") .append(" th { background-color: #f2f2f2; }\n") .append("</style>\n") .append("</head>\n<body>\n") .append("<table>\n"); // 3. 遍历所有工作表 for (int i = 0; i < workbook.getNumberOfSheets(); i++) { Sheet sheet = workbook.getSheetAt(i); // 如果不是第一个工作表,添加一个标题 if (i > 0) { htmlBuilder.append("</table>\n<h2>").append(sheet.getSheetName()).append("</h2>\n<table>\n"); } else { htmlBuilder.append("<caption>").append(sheet.getSheetName()).append("</caption>\n"); } // 用于缓存样式,避免重复定义 Map<CellStyle, String> styleCache = new HashMap<>(); // 4. 遍历每一行 for (Row row : sheet) { htmlBuilder.append("<tr>\n"); // 5. 遍历每一个单元格 for (Cell cell : row) { htmlBuilder.append("<td"); // 获取单元格样式并应用 CellStyle style = cell.getCellStyle(); String styleString = styleCache.computeIfAbsent(style, s -> { StringBuilder sb = new StringBuilder(); // 字体 Font font = workbook.getFontAt(style.getFontIndexAsInt()); sb.append(" font-family:").append(font.getFontName()).append(";"); sb.append(" font-size:").append(font.getFontHeightInPoints()).append("pt;"); sb.append(" font-weight:").append(font.getBold() ? "bold" : "normal").append(";"); sb.append(" font-style:").append(font.getItalic() ? "italic" : "normal").append(";"); sb.append(" color:").append(getColorHex(font.getColor())).append(";"); // 背景色 if (style.getFillForegroundColor() != IndexedColors.AUTOMATIC.getIndex()) { sb.append(" background-color:").append(getColorHex(style.getFillForegroundColor())).append(";"); } // 对齐方式 sb.append(" text-align:"); switch (style.getAlignment()) { case CENTER: sb.append("center;"); break; case RIGHT: sb.append("right;"); break; default: sb.append("left;"); break; } // 边框 sb.append(" border: 1px solid #ddd;"); // 简化边框处理 return sb.toString(); }); htmlBuilder.append(" style=\"").append(styleString).append("\""); // 合并单元格的处理 if (sheet.isMergedRegion(row.getRowNum())) { for (CellRangeAddress region : sheet.getMergedRegions()) { if (region.isInRange(row.getRowNum(), cell.getColumnIndex())) { int colspan = region.getLastColumn() - region.getFirstColumn() + 1; int rowspan = region.getLastRow() - region.getFirstRow() + 1; htmlBuilder.append(" colspan=\"").append(colspan).append("\""); htmlBuilder.append(" rowspan=\"").append(rowspan).append("\""); break; } } } htmlBuilder.append(">"); // 6. 获取单元格值 switch (cell.getCellType()) { case STRING: htmlBuilder.append(cell.getStringCellValue()); break; case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { htmlBuilder.append(cell.getDateCellValue().toString()); } else { htmlBuilder.append(cell.getNumericCellValue()); } break; case BOOLEAN: htmlBuilder.append(cell.getBooleanCellValue()); break; case FORMULA: // 也可以尝试计算公式结果: cell.getNumericCellValue() htmlBuilder.append("= ").append(cell.getCellFormula()); break; case BLANK: // 空单元格,输出空格以保持表格结构 htmlBuilder.append(" "); break; default: htmlBuilder.append("N/A"); } htmlBuilder.append("</td>\n"); } htmlBuilder.append("</tr>\n"); } } // 7. 构建 HTML 尾部 htmlBuilder.append("</table>\n</body>\n</html>"); // 8. 写入 HTML 文件 try (BufferedWriter writer = new BufferedWriter(new FileWriter(htmlFilePath))) { writer.write(htmlBuilder.toString()); } } } /** * 将 POI 的 IndexedColor 值转换为十六进制颜色字符串 */ private static String getColorHex(short indexedColor) { // 这是一个简化的实现,实际 POI 中有更完整的颜色映射 // 这里只返回一个占位符,实际项目中应使用完整的颜色映射表 return "#" + Integer.toHexString(indexedColor).substring(2); } }
使用 EasyExcel (推荐,阿里巴巴出品)
EasyExcel 是阿里巴巴开源的、基于 POI 的一个工具库,它解决了 POI 内存占用高的问题,尤其适合处理大文件,并且提供了更简洁的 API。

优点:
- API 简洁:读和写的 API 都非常直观,易于上手。
- 性能优越:采用 SAX 模式读取,内存占用极低,适合处理百万行级别的 Excel。
- 功能强大:除了读写,也提供了模板导出、填充等高级功能。
缺点:
- 样式支持相对较弱:虽然支持,但相比 POI 原生 API,对复杂样式的控制能力稍逊一筹。
- 主要关注读写:虽然可以转换,但其核心定位是读写工具,而不是专门的转换工具。
实现步骤
-
添加 Maven 依赖
<dependencies> <!-- EasyExcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>3.3.2</version> </dependency> <!-- EasyExcel 内部依赖了 POI --> </dependencies> -
编写 Java 代码
EasyExcel 没有直接提供“转 HTML”的现成方法,但它提供了强大的
AnalysisEventListener(分析监听器),我们可以在监听器中处理每一行数据并构建 HTML,这种方法更灵活,但需要自己处理表格结构。import com.alibaba.excel.EasyExcel; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.read.metadata.ReadRowHolder; import com.alibaba.excel.read.metadata.ReadSheet; import org.apache.poi.ss.usermodel.*; import java.io.BufferedWriter; java.io.FileInputStream; java.io.FileWriter; import java.io.IOException; import java.util.HashMap; java.util.Map; public class EasyExcelToHtmlConverter { public static void main(String[] args) { String excelFilePath = "input.xlsx"; String htmlFilePath = "output_easyexcel.html"; try { convertExcelToHtml(excelFilePath, htmlFilePath); System.out.println("EasyExcel 转换成功!HTML 文件已生成于: " + htmlFilePath); } catch (IOException e) { e.printStackTrace(); } } public static void convertExcelToHtml(String excelFilePath, String htmlFilePath) throws IOException { // 使用 POI 的 Workbook 来获取完整的样式信息,因为 EasyExcel 的监听器获取样式信息有限 try (Workbook workbook = WorkbookFactory.create(new FileInputStream(excelFilePath))) { StringBuilder htmlBuilder = new StringBuilder(); htmlBuilder.append("<!DOCTYPE html>\n<html>\n<head>\n") .append("<meta charset=\"UTF-8\">\n<title>EasyExcel to HTML</title>\n") .append("<style>table,td{border:1px solid #ddd;border-collapse:collapse;padding:8px;}</style>\n") .append("</head>\n<body>\n<table>\n"); Map<Integer, String> styleCache = new HashMap<>(); // EasyExcel 读取 EasyExcel.read(excelFilePath, new AnalysisEventListener<Map<Integer, String>>() { private int currentSheetIndex = -1; private int currentRowNum = -1; @Override public void invokeHead(Map<Integer, String> headMap, ReadRowHolder readRowHolder) { // 处理表头 currentSheetIndex = readRowHolder.getSheetNo(); currentRowNum = readRowHolder.getRowNum(); htmlBuilder.append("<tr>\n"); for (int i = 0; i < headMap.size(); i++) { htmlBuilder.append("<th>").append(headMap.getOrDefault(i, "")).append("</th>\n"); } htmlBuilder.append("</tr>\n"); } @Override public void invoke(Map<Integer, String> rowData, AnalysisContext context) { // 处理数据行 // 检查是否是新工作表的开始 if (context.readRowHolder().getSheetNo() != currentSheetIndex) { // ... 处理新工作表的逻辑 ... // 为简化,这里只处理第一个工作表 return; } // 检查是否是新行的开始 if (context.readRowHolder().getRowIndex() != currentRowNum + 1) { // ... 处理新行的逻辑 ... // 为简化,这里假设行是连续的 } currentRowNum = context.readRowHolder().getRowIndex(); htmlBuilder.append("<tr>\n"); for (int i = 0; i < rowData.size(); i++) { htmlBuilder.append("<td>").append(rowData.getOrDefault(i, " ")).append("</td>\n"); } htmlBuilder.append("</tr>\n"); } @Override public void doAfterAllAnalysed(AnalysisContext context) { // 所有数据解析完成后的操作 // 这里可以添加关闭表格标签等 } }).sheet().doRead(); htmlBuilder.append("</table>\n</body>\n</html>"); try (BufferedWriter writer = new BufferedWriter(new FileWriter(htmlFilePath))) { writer.write(htmlBuilder.toString()); } } } }
注意:上面的 EasyExcel 示例为了简化,省略了对复杂样式和合并单元格的完整处理,EasyExcel 的优势在于数据读取,如果你需要高保真地转换样式,结合 POI 来获取样式信息,然后用 EasyExcel 来流式读取数据 是一个不错的折中方案。
使用商业库 (如 Aspose.Cells)
除了开源库,还有一些商业库提供非常强大和简单的转换功能。
优点:
- API 极其简单:通常一行代码就能完成转换。
- 保真度极高:能完美还原 Excel 的外观,包括复杂的样式、图表、图片、页眉页脚等。
- 性能和稳定性好:商业库通常经过严格测试,性能优异。
缺点:
- 收费:需要购买许可证,对于个人或小项目可能成本较高。
- 闭源:无法查看源码,定制化能力受限。
示例代码 (Aspose.Cells)
import com.aspose.cells.*;
public class AsposeExcelToHtml {
public static void main(String[] args) throws Exception {
// 加载 Excel 文件
Workbook workbook = new Workbook("input.xlsx");
// 创建 HtmlSaveOptions 对象
HtmlSaveOptions options = new HtmlSaveOptions();
// 可以设置一些选项,例如将 CSS 样式内联
options.setCssStyles(CssStylesType.INLINE);
// 保存为 HTML
workbook.save("output_aspose.html", options);
System.out.println("Aspose.Cells 转换成功!");
}
}
可以看到,使用 Aspose.Cells 的代码量非常少,功能非常强大。
总结与选择建议
| 特性 | Apache POI | EasyExcel | Aspose.Cells (商业) |
|---|---|---|---|
| 易用性 | 中等 (需要较多代码) | 高 (API 简洁) | 极高 (一行代码) |
| 功能/保真度 | 极高 (完全控制) | 中等 (样式支持较弱) | 极高 (完美还原) |
| 性能 | 一般 (大文件内存占用高) | 极高 (流式读取) | 优秀 |
| 成本 | 免费 | 免费 | 收费 |
| 适用场景 | 需要精细控制样式、处理复杂 Excel 结构、免费项目 | 大文件处理、数据读写为主、免费项目 | 追求极致便利和保真度、预算充足的项目 |
如何选择?
-
如果你是个人开发者或在做小型项目,且需要免费方案:
- 如果你的 Excel 文件不大,且需要精确控制样式和布局,选择 Apache POI,虽然代码多一点,但最灵活、最可靠。
- 如果你的 Excel 文件非常大(几十MB甚至GB级别),选择 EasyExcel,它能有效防止内存溢出。
-
如果你是公司项目,预算充足,且追求开发效率和转换质量:
- 毫不犹豫选择 Aspose.Cells,它能为你节省大量开发和测试时间,转换效果也最好。
对于大多数初学者和中小型项目,从 Apache POI 开始学习是最好的选择,因为它能让你深刻理解 Excel 到 HTML 的转换原理。
