1. 概述

本文将演示如何使用Spring MVC 框架上传Excel文件并在网页上显示其内容。

2. 上传Excel文件

首先,我们需要创建一个控制器映射,接收一个MultipartFile并将其保存在当前位置:

private String fileLocation;

@PostMapping("/uploadExcelFile")
public String uploadFile(Model model, MultipartFile file) throws IOException {
    InputStream in = file.getInputStream();
    File currDir = new File(".");
    String path = currDir.getAbsolutePath();
    fileLocation = path.substring(0, path.length() - 1) + file.getOriginalFilename();
    FileOutputStream f = new FileOutputStream(fileLocation);
    int ch = 0;
    while ((ch = in.read()) != -1) {
        f.write(ch);
    }
    f.flush();
    f.close();
    model.addAttribute("message", "File: " + file.getOriginalFilename() 
      + " has been uploaded successfully!");
    return "excel";
}

接下来,我们将在一个JSP文件中创建一个表单,包含一个type="file"input元素,其accept属性仅允许上传Excel文件:

<c:url value="/uploadExcelFile" var="uploadFileUrl" />
<form method="post" enctype="multipart/form-data"
  action="${uploadFileUrl}">
    <input type="file" name="file" accept=".xls,.xlsx" /> <input
      type="submit" value="Upload file" />
</form>

3. 读取Excel文件

为了解析上传的Excel文件,我们将使用Apache POI 库,它支持.xls.xlsx文件。

让我们创建一个辅助类MyCell,其中包含与Excel单元格内容和格式相关的属性:

public class MyCell {
    private String content;
    private String textColor;
    private String bgColor;
    private String textSize;
    private String textWeight;

    public MyCell(String content) {
        this.content = content;
    }
    
    //standard constructor, getters, setters
}

我们将读取Excel文件的内容到一个包含MyCell对象列表的地图中。

3.1. 解析.xls文件

.xls文件在Apache POI库中由HSSFWorkbook类表示,由HSSFSheet对象组成。有关如何使用Java处理Microsoft Excel,请参阅此篇文章

要解析单元格格式,我们将获取HSSFCellStyle对象,这有助于我们确定如背景颜色和字体等属性。所有读取的属性都将设置在MyCell对象的属性中:

HSSFCellStyle cellStyle = cell.getCellStyle();

MyCell myCell = new MyCell();

HSSFColor bgColor = cellStyle.getFillForegroundColorColor();
if (bgColor != null) {
    short[] rgbColor = bgColor.getTriplet();
    myCell.setBgColor("rgb(" + rgbColor[0] + ","
      + rgbColor[1] + "," + rgbColor[2] + ")");
    }
HSSFFont font = cell.getCellStyle().getFont(workbook);

颜色以rgb(rVal, gVal, bVal)格式读取,以便在JSP页面中使用CSS更轻松地显示。

还要获取字体大小、粗细和颜色:

myCell.setTextSize(font.getFontHeightInPoints() + "");
if (font.getBold()) {
    myCell.setTextWeight("bold");
}
HSSFColor textColor = font.getHSSFColor(workbook);
if (textColor != null) {
    short[] rgbColor = textColor.getTriplet();
    myCell.setTextColor("rgb(" + rgbColor[0] + ","
      + rgbColor[1] + "," + rgbColor[2] + ")");
}

3.2. 解析.xlsx文件

对于.xlsx格式的文件,我们可以使用XSSFWorkbook类以及文档中所述的其他内容。

让我们详细看看.xlsx格式下单元格格式的读取。首先,我们将获取与单元格关联的XSSFCellStyle对象,并用它来确定背景颜色和字体:

XSSFCellStyle cellStyle = cell.getCellStyle();

MyCell myCell = new MyCell();
XSSFColor bgColor = cellStyle.getFillForegroundColorColor();
if (bgColor != null) {
    byte[] rgbColor = bgColor.getRGB();
    myCell.setBgColor("rgb(" 
      + (rgbColor[0] < 0 ? (rgbColor[0] + 0xff) : rgbColor[0]) + ","
      + (rgbColor[1] < 0 ? (rgbColor[1] + 0xff) : rgbColor[1]) + ","
      + (rgbColor[2] < 0 ? (rgbColor[2] + 0xff) : rgbColor[2]) + ")");
}
XSSFFont font = cellStyle.getFont();

在这种情况下,颜色的RGB值是带符号的字节值,我们将通过将负值加上0xff来获取无符号值。

让我们也确定字体属性:

myCell.setTextSize(font.getFontHeightInPoints() + "");
if (font.getBold()) {
    myCell.setTextWeight("bold");
}
XSSFColor textColor = font.getXSSFColor();
if (textColor != null) {
    byte[] rgbColor = textColor.getRGB();
    myCell.setTextColor("rgb("
      + (rgbColor[0] < 0 ? (rgbColor[0] + 0xff) : rgbColor[0]) + "," 
      + (rgbColor[1] < 0 ? (rgbColor[1] + 0xff) : rgbColor[1]) + "," 
      + (rgbColor[2] < 0 ? (rgbColor[2] + 0xff) : rgbColor[2]) + ")");
}

3.3. 处理空行

上述方法没有考虑Excel文件中的空行。如果我们希望准确地展示文件,包括空行,我们需要在结果HashMap中模拟这些空行,其中包含一个包含空字符串的ArrayList作为内容。

最初,读取文件后,文件中的空行将是大小为0的ArrayList对象。

为了确定应添加多少个空字符串,我们将首先使用maxNrCols变量确定Excel文件中最长的行数。然后,我们将为所有在HashMap中大小为0的列表添加相应数量的空字符串:

int maxNrCols = data.values().stream()
  .mapToInt(List::size)
  .max()
  .orElse(0);

data.values().stream()
  .filter(ls -> ls.size() < maxNrCols)
  .forEach(ls -> {
      IntStream.range(ls.size(), maxNrCols)
        .forEach(i -> ls.add(new MyCell("")));
  });

4. 显示Excel文件

为了使用Spring MVC 显示使用Java读取的Excel文件,我们需要定义一个控制器映射和JSP页面。

4.1. Spring MVC 控制器

创建一个@RequestMapping方法,该方法将调用上面的代码来读取文件内容,并将返回的地图作为模型属性:

@Resource(name = "excelPOIHelper")
private ExcelPOIHelper excelPOIHelper;

@RequestMapping(method = RequestMethod.GET, value = "/readPOI")
public String readPOI(Model model) throws IOException {

  if (fileLocation != null) {
      if (fileLocation.endsWith(".xlsx") || fileLocation.endsWith(".xls")) {
          Map<Integer, List<MyCell>> data
            = excelPOIHelper.readExcel(fileLocation);
          model.addAttribute("data", data);
      } else {
          model.addAttribute("message", "Not a valid excel file!");
      }
  } else {
      model.addAttribute("message", "File missing! Please upload an excel file.");
  }
  return "excel";
}

4.2. JSP

为了可视化文件内容,我们将创建一个HTML表格,并在每个表格单元格的style属性中添加对应于Excel文件中每个单元格的格式属性:

<c:if test="${not empty data}">
    <table style="border: 1px solid black; border-collapse: collapse;">
        <c:forEach items="${data}" var="row">
            <tr>
                <c:forEach items="${row.value}" var="cell">
                    <td style="border:1px solid black;height:20px;width:100px;
                      background-color:${cell.bgColor};color:${cell.textColor};
                      font-weight:${cell.textWeight};font-size:${cell.textSize}pt;">
                      ${cell.content}
                    </td>
                </c:forEach>
            </tr>
        </c:forEach>
    </table>
</c:if>

5. 总结

本文提供了一个示例项目,展示了如何使用Spring MVC框架上传Excel文件并在网页上显示它们。完整源代码可在GitHub项目中找到。