太強了!Spring Boot 3.3 一個接口就能搞定 Excel 導入導出所有表!
在日常的企業系統或后臺管理系統中,數據的 Excel 導入導出是非常常見的需求。傳統方式通常是:
- 每張表都寫一個專門的導入導出方法;
- 每張表都建立一個 Java Bean 類,硬編碼字段;
- 新增或修改表結構時需要修改大量代碼。
這些方式帶來的問題有:代碼重復多、維護成本高、靈活性差。
因此,本文基于 Spring Boot 3.3 + EasyExcel 實現一個 "支持任意表結構、無需綁定實體類、異步處理大文件導入" 的通用 Excel 導入導出功能。
技術選型與優勢
技術 | 用途 |
Spring Boot 3.3 | 構建 RESTful Web 項目 |
EasyExcel | 快速讀取/寫入 Excel 文件 |
JdbcTemplate | 動態操作任意表結構 |
ThreadPool | 支持異步導入,釋放主線程 |
數據庫準備(支持任意表結構)
-- 用戶表
CREATETABLEuser(
id BIGINTPRIMARYKEYAUTO_INCREMENT,
name VARCHAR(100),
phone VARCHAR(50),
id_card VARCHAR(50),
created_at DATETIMEDEFAULTCURRENT_TIMESTAMP
);
-- 商品表
CREATETABLE product (
id BIGINTPRIMARYKEYAUTO_INCREMENT,
name VARCHAR(100),
price DECIMAL(10,2),
stock INT,
created_at DATETIMEDEFAULTCURRENT_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平臺或需要支持可配置表單數據導入導出功能的系統,這種通用設計無疑能大大提升系統的靈活性和擴展性。