在后台管理系统中,Excel导入导出是出现频率最高的功能之一。无论是导出员工信息、订单报表,还是批量导入基础数据,如果每次都要手动拼接文件流或第三方工具,很容易写出重复且难维护的代码。本文将以Spring Boot为框架,Apache POI为底层库,逐步实现一个包含导出与导入的完整示例,并给出可以直接运行的代码片段。

一、项目依赖与数据模型准备
首先创建一个Spring Boot项目,在pom.xml中引入Apache POI的xlsx处理依赖。POI提供了HSSFWorkbook处理旧版xls格式,而XSSFWorkbook用来处理xlsx格式。由于目前主流Excel文件均为xlsx,本文选择poi-ooxml。依赖配置如下:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
引入该依赖后,poi基础包和poi-ooxml-lite等传递依赖会自动加入。需要注意的是,POI 5.x要求JDK 8及以上版本,如果项目中已经使用了JDK 17,也可以正常兼容。接下来定义一个员工实体类Employee,用于承载导入导出过程中的数据。
import java.math.BigDecimal;
import java.util.Date;
public class Employee {
private Long id;
private String name;
private String department;
private BigDecimal salary;
private Date hireDate;
// 省略getter和setter
}
实体类字段与Excel列一一对应,包括员工编号、姓名、部门、薪资和入职日期。实际项目中可以根据业务需要增加注解或校验规则,这里保持简单以便聚焦于POI操作本身。后续的导出和导入方法都将围绕Employee列表展开。
二、使用Apache POI实现Excel导出
导出功能的核心思路是:先从数据库查询出员工列表,然后创建XSSFWorkbook工作簿对象,在工作簿中创建Sheet,再逐行写入表头和员工数据,最后将工作簿写入HttpServletResponse输出流。下面是一个完整的导出方法示例:
public void exportEmployees(List<Employee> employees, HttpServletResponse response) throws IOException {
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFSheet sheet = workbook.createSheet("员工信息");
// 创建表头
XSSFRow headerRow = sheet.createRow(0);
String[] headers = {"员工编号", "姓名", "部门", "薪资", "入职日期"};
for (int i = 0; i < headers.length; i++) {
XSSFCell cell = headerRow.createCell(i);
cell.setCellValue(headers[i]);
XSSFCellStyle style = workbook.createCellStyle();
XSSFFont font = workbook.createFont();
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
cell.setCellStyle(style);
}
// 写入数据
int rowIndex = 1;
for (Employee emp : employees) {
XSSFRow row = sheet.createRow(rowIndex);
row.createCell(0).setCellValue(emp.getId());
row.createCell(1).setCellValue(emp.getName());
row.createCell(2).setCellValue(emp.getDepartment());
row.createCell(3).setCellValue(emp.getSalary().doubleValue());
XSSFCell dateCell = row.createCell(4);
dateCell.setCellValue(emp.getHireDate());
XSSFCellStyle dateStyle = workbook.createCellStyle();
dateStyle.setDataFormat(workbook.getCreationHelper().createDataFormat().getFormat("yyyy-MM-dd"));
dateCell.setCellStyle(dateStyle);
rowIndex++;
}
// 自动调整列宽
for (int i = 0; i < headers.length; i++) {
sheet.autoSizeColumn(i);
}
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
String fileName = "员工信息.xlsx";
String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
OutputStream out = response.getOutputStream();
workbook.write(out);
workbook.close();
out.flush();
out.close();
}
代码中为表头设置了加粗和居中样式,并通过autoSizeColumn自动调整列宽。日期列使用POI内置的数据格式yyyy-MM-dd,这样导出的单元格会以文本形式显示日期,而不是一串数字。响应头Content-Disposition使用filename*=UTF-8''格式编码中文文件名,可以兼容大多数现代浏览器,避免下载时出现乱码。
如果薪资字段在数据库中以BigDecimal存储,建议先调用doubleValue()再写入单元格,否则直接传入BigDecimal会被POI当作文本处理,在Excel中无法正确进行数值计算。同样地,编号字段如果是Long类型,也可以直接写入数值单元格,但要注意Excel中超过15位的数字会出现精度丢失,必要时可以设置为文本格式。
三、使用Apache POI实现Excel导入
导入功能需要接收前端上传的Excel文件,解析工作簿后逐行读取单元格数据,并映射为Employee对象。解析时不能假设所有单元格都是字符串类型,需要根据CellType分别处理数值、字符串和日期。下面给出一个导入方法的完整实现:
public List<Employee> importEmployees(MultipartFile file) throws IOException {
List<Employee> employees = new ArrayList<>();
InputStream inputStream = file.getInputStream();
XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
XSSFSheet sheet = workbook.getSheetAt(0);
for (int rowIndex = 1; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
XSSFRow row = sheet.getRow(rowIndex);
if (row == null) {
continue;
}
Employee emp = new Employee();
emp.setId((long) getNumericValue(row.getCell(0)));
emp.setName(getStringValue(row.getCell(1)));
emp.setDepartment(getStringValue(row.getCell(2)));
emp.setSalary(new BigDecimal(getNumericValue(row.getCell(3))));
emp.setHireDate(row.getCell(4).getDateCellValue());
employees.add(emp);
}
workbook.close();
inputStream.close();
return employees;
}
private String getStringValue(XSSFCell cell) {
if (cell == null) {
return "";
}
if (cell.getCellType() == CellType.STRING) {
return cell.getStringCellValue();
} else if (cell.getCellType() == CellType.NUMERIC) {
return String.valueOf(cell.getNumericCellValue());
} else {
return "";
}
}
private double getNumericValue(XSSFCell cell) {
if (cell == null) {
return 0;
}
if (cell.getCellType() == CellType.NUMERIC) {
return cell.getNumericCellValue();
} else if (cell.getCellType() == CellType.STRING) {
return Double.parseDouble(cell.getStringCellValue());
} else {
return 0;
}
}
上述代码默认Excel第一行是表头,因此从rowIndex = 1开始读取。对于空行直接跳过,避免出现空指针异常。获取字符串时兼容了数值单元格,例如员工编号如果被Excel自动转为数字,可以通过getNumericCellValue取回原始数值。日期单元格直接调用getDateCellValue即可,但前提是该单元格格式为日期类型。
实际项目中,导入还需要做必要的数据校验。比如姓名和部门不能为空,薪资必须大于0,入职日期不能晚于当前日期等。建议在解析完每行数据后立即进行字段级校验,并收集错误信息,最后统一返回给前端,而不是遇到第一行错误就中断整个导入流程。
四、Controller接口与测试验证
在Controller层暴露两个接口:一个用于导出Excel,另一个用于导入Excel。导出接口返回文件流,导入接口接收MultipartFile。示例代码如下:
@RestController
@RequestMapping("/employee")
public class EmployeeController {
private final EmployeeService employeeService;
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
@GetMapping("/export")
public void export(HttpServletResponse response) throws IOException {
List<Employee> employees = employeeService.listAll();
employeeService.exportEmployees(employees, response);
}
@PostMapping("/import")
public String importExcel(@RequestParam("file") MultipartFile file) throws IOException {
List<Employee> employees = employeeService.importEmployees(file);
employeeService.saveBatch(employees);
return "导入成功,共处理" + employees.size() + "条记录";
}
}
测试导出接口时,可以直接在浏览器访问/employee/export,浏览器会自动下载Excel文件。导入接口则需要通过Postman或前端表单上传文件,参数名必须为file,并且请求方式为POST。如果使用Swagger调试,选择form-data类型并上传xlsx文件即可。
为了让导入失败时也能返回清晰的信息,可以在Service层捕获IOException或自定义业务异常,并在Controller中使用@ExceptionHandler统一处理。比如文件格式错误、文件内容为空、必填字段缺失等,都可以封装成统一的错误响应结构,方便前端展示。
五、大数据量优化与安全注意事项
当需要导出几万甚至几十万条数据时,使用XSSFWorkbook会将整个工作簿对象保留在内存中,容易引发OutOfMemoryError。针对这种情况,POI提供了SXSSFWorkbook,它只保留固定数量的行在内存中,其余行会写入临时文件,从而大幅降低内存占用。使用时只需将XSSFWorkbook替换为SXSSFWorkbook,基本API保持不变。阿里开源的EasyExcel也是不错的选择,它基于SAX模式解析,导入导出速度更快、内存占用更低,适合超大文件场景。
导入Excel时还要注意安全性问题。Excel单元格中如果包含以=开头的公式,当用户用Excel打开文件时有可能触发公式计算,存在公式注入风险。建议在解析字符串单元格时,先判断值是否以=、+、-、@开头,如果属于风险字符,可以在写入数据库前进行转义或直接拒绝该行数据。同时,上传文件的大小也需要限制,可在application.yml中配置spring.servlet.multipart.max-file-size和max-request-size,防止恶意上传超大文件导致服务器资源耗尽。
本文从零开始演示了Spring Boot结合Apache POI实现Excel导入导出的完整过程,包括依赖配置、实体设计、导出服务、导入解析、Controller接口以及大数据量优化。代码示例可直接复制使用,只需要根据自己的实体字段调整列名和读取逻辑。掌握这些基础后,再根据业务需求封装通用工具类,就能显著提升开发效率。
SpringBootExcel导入导出Apache POI修改时间:2026-09-25 08:04:57