导读:本期聚焦于追梦人创作的《SpringBoot如何高效实现Excel导入导出?完整代码实例与步骤详解》,敬请观看详情。业务系统中报表导出和数据批量录入的需求非常常见,如果直接操作文件流会涉及大量底层细节。本文以Spring Boot框架为基础,结合Apache POI的XSSF组件,从项目依赖配置、实体类设计到导出服务、导入解析、Controller接口,完整演示一套可直接运行的Excel导入导出方案。导出部分重点处理表头样式、列宽自适应、日期格式和中文文件名乱码;导入部分则覆盖单元格类型判断、日期解析、必填校验和异常提示。代码全部给出,步骤清晰,读者照着修改即可用于自己的项目。文末还补充了大数据量场景下使用SXSSFWorkbook和EasyExcel的优化建议,以及导入时防止公式注入等安全注意事项。通过本文可以快速掌握Spring Boot操作Excel的常用实现方式。

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

SpringBoot如何高效实现Excel导入导出?完整代码实例与步骤详解

一、项目依赖与数据模型准备

首先创建一个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

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0925/61629.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。