Spring Boot 3.3 一个接口就能搞定 Excel 导入导出所有表!

6 阅读2分钟

在日常的企业系统或后台管理系统中,数据的 Excel 导入导出是非常常见的需求。传统方式通常是:

  • 每张表都写一个专门的导入导出方法;
  • 每张表都建立一个 Java Bean 类,硬编码字段;
  • 新增或修改表结构时需要修改大量代码。

这些方式带来的问题有:代码重复多、维护成本高、灵活性差

因此,本文基于 Spring Boot 3.3 + EasyExcel 实现一个 "支持任意表结构、无需绑定实体类、异步处理大文件导入" 的通用 Excel 导入导出功能。

技术选型与优势

技术用途
Spring Boot 3.3构建 RESTful Web 项目
EasyExcel快速读取/写入 Excel 文件
JdbcTemplate动态操作任意表结构
ThreadPool支持异步导入,释放主线程

数据库准备(支持任意表结构)

-- 用户表
CREATE TABLE user(
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100),
  phone VARCHAR(50),
  id_card VARCHAR(50),
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 商品表
CREATETABLE product (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100),
  price DECIMAL(10,2),
  stock INT,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

通用导入导出接口设计

@RestController
@RequestMapping("/excel")
@RequiredArgsConstructor
public class ExcelController {


    private final JdbcTemplate jdbcTemplate;
    private final ThreadPoolTaskExecutor taskExecutor;


    /**
     * Excel 导入任意表(异步)
     */
    @PostMapping("/import")
    public ResponseEntity<String> importExcel(@RequestParam("file") MultipartFile file,
                                              @RequestParam("tableName") String tableName) throws IOException {


        List<Map<Integer, String>> rowData = new ArrayList<>();


        EasyExcel.read(file.getInputStream(), new AnalysisEventListener<Map<Integer, String>>() {
            @Override
            public void invoke(Map<Integer, String> data, AnalysisContext context) {
                rowData.add(data);
            }


            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {}
        }).sheet().doRead();


        // 获取目标表字段名(排除主键)
        List<String> columns = jdbcTemplate.queryForList(
                "SHOW COLUMNS FROM " + tableName + " WHERE Field != 'id'", String.class);


        if (columns.size() != rowData.get(0).size()) {
            return ResponseEntity.badRequest().body("Excel列数与表字段不匹配");
        }


        // 异步处理
        taskExecutor.execute(() -> {
            for (Map<Integer, String> row : rowData) {
                String sql = "INSERT INTO " + tableName + " (" + String.join(",", columns) + ") VALUES (" +
                        String.join(",", Collections.nCopies(columns.size(), "?")) + ")";
                Object[] values = columns.stream().map(col -> row.get(columns.indexOf(col))).toArray();
                jdbcTemplate.update(sql, values);
            }
        });


        return ResponseEntity.ok("文件上传成功,已异步导入中");
    }


    /**
     * Excel 导出任意表
     */
    @GetMapping("/export")
    public void exportExcel(@RequestParam("tableName") String tableName, HttpServletResponse response) throws IOException {
        List<String> columnNames = jdbcTemplate.queryForList("SHOW COLUMNS FROM " + tableName, String.class);
        List<Map<String, Object>> rows = jdbcTemplate.queryForList("SELECT * FROM " + tableName);


        List<List<String>> excelData = new ArrayList<>();
        excelData.add(columnNames);


        for (Map<String, Object> row : rows) {
            List<String> rowList = columnNames.stream().map(col -> {
                Object value = row.get(col);
                return value == null ? "" : value.toString();
            }).collect(Collectors.toList());
            excelData.add(rowList);
        }


        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode(tableName + "_export.xlsx", "UTF-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);


        EasyExcel.write(response.getOutputStream())
                .sheet("数据")
                .doWrite(excelData);
    }
}

线程池配置支持异步导入

@Configuration
public class ThreadConfig {
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
        pool.setCorePoolSize(4);
        pool.setMaxPoolSize(8);
        pool.setQueueCapacity(100);
        pool.setKeepAliveSeconds(30);
        pool.setThreadNamePrefix("excel-import-");
        pool.initialize();
        return pool;
    }
}

spring:
  task:
    execution:
      pool:
        core-size: 4
        max-size: 8
        queue-capacity: 100

前端 Thymeleaf 示例

<form method="post" enctype="multipart/form-data" action="/excel/import">
    <input type="file" name="file">
    <input type="text" name="tableName" placeholder="输入表名">
    <button type="submit">导入 Excel</button>
</form>

<a href="/excel/export?tableName=user">导出用户表</a>

总结:如何提升系统通用能力

通过本文的设计与实战,我们实现了一个通用 Excel 导入导出框架,具备如下优势:

  • ✅ 高通用性:支持任意数据库表结构导入导出
  • ✅ 低维护成本:无需重复写实体类和 mapper
  • ✅ 异步处理能力:导入可处理大文件不阻塞主线程
  • ✅ 适配前后端分离/低代码平台使用场景

如果你正在开发一个后台系统、BI平台或需要支持可配置表单数据导入导出功能的系统,这种通用设计无疑能大大提升系统的灵活性和扩展性。