SpringBoot + Apache POI 实现数据导入导出

功能特点:

  1. 智能列匹配:
    支持精确列名匹配
    支持忽略大小写的列名匹配
    自动匹配字段名(当未指定@ExcelProperty时)
    强大的类型转换:
    支持基本数据类型(Integer/Long/Double等)
    支持日期类型(Date/LocalDate/LocalDateTime)
    支持自定义日期格式
    自动处理公式单元格
    支持布尔值智能转换(“是/否”、“1/0”、“true/false”)
  2. 容错处理:
    跳过空行
    记录错误行信息
    单行错误不影响其他数据导入
    支持严格/宽容两种模式
  3. 扩展性:
    支持通用导入接口(通过类名指定目标类型)
    返回详细导入结果(成功数据+错误信息)
    可扩展支持CSV等格式

1.公共导入接口开发

使用示例:

  1. 准备Excel文件(首行为列名):
| 用户ID | 用户名 | 注册日期   | 最后登录时间       | 账户状态 |
|--------|--------|------------|-------------------|----------|
| 1      | 张三   | 2023-01-15 | 2023/06/30 09:30 ||
| 2      | 李四   | 2023-02-20 | 2023/06/29 14:15 ||
  1. 通过HTML页面上传文件
  2. 服务端返回导入结果:
{"totalCount": 2,"successCount": 2,"successData": [{"id": 1,"username": "张三","registerDate": "2023-01-15","lastLogin": "2023-06-30T09:30","active": true},{"id": 2,"username": "李四","registerDate": "2023-02-20","lastLogin": "2023-06-29T14:15","active": false}],"errorMessages": []
}

注意事项:

  1. 大文件处理建议:
// 使用SXSSFWorkbook处理大文件
Workbook workbook = new SXSSFWorkbook(new XSSFWorkbook(inputStream), 100);

1.增强自定义注解(添加日期格式支持)

import java.lang.annotation.*;// 列映射注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelProperty {// 列名String value() default "";// 日期格式(仅对时间类型有效)String dateFormat() default "yyyy-MM-dd HH:mm:ss";
}// 忽略字段注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelIgnore {}

2.创建通用导入工具类

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;public class ExcelImportUtil {/*** 通用Excel导入方法* @param file  上传的文件* @param clazz 目标对象类型* @return 导入结果对象列表*/public static <T> List<T> importExcel(MultipartFile file, Class<T> clazz) throws IOException {List<T> resultList = new ArrayList<>();Map<String, Field> fieldMap = getFieldMap(clazz);try (InputStream inputStream = file.getInputStream();Workbook workbook = new XSSFWorkbook(inputStream)) {Sheet sheet = workbook.getSheetAt(0);Row headerRow = sheet.getRow(0);// 1. 构建列名到字段的映射Map<Integer, FieldMapping> columnMapping = new HashMap<>();for (int col = 0; col < headerRow.getLastCellNum(); col++) {Cell cell = headerRow.getCell(col);if (cell != null) {String columnName = cell.getStringCellValue().trim();Field field = findFieldByColumnName(fieldMap, columnName);if (field != null) {columnMapping.put(col, new FieldMapping(field, getDateFormat(field)));}}}// 2. 处理数据行for (int rowNum = 1; rowNum <= sheet.getLastRowNum(); rowNum++) {Row row = sheet.getRow(rowNum);if (row == null) continue;try {T obj = clazz.getDeclaredConstructor().newInstance();boolean hasData = false;for (int col = 0; col < headerRow.getLastCellNum(); col++) {Cell cell = row.getCell(col);if (cell == null) continue;FieldMapping mapping = columnMapping.get(col);if (mapping != null) {Object value = parseCellValue(cell, mapping.field.getType(), mapping.dateFormat);if (value != null) {mapping.field.setAccessible(true);mapping.field.set(obj, value);hasData = true;}}}if (hasData) {resultList.add(obj);}} catch (Exception e) {// 记录错误行信息(可扩展为错误收集)System.err.printf("导入第 %d 行数据出错: %s%n", rowNum + 1, e.getMessage());}}} catch (Exception e) {throw new IOException("文件解析失败: " + e.getMessage(), e);}return resultList;}// 获取字段映射(列名->字段)private static Map<String, Field> getFieldMap(Class<?> clazz) {Map<String, Field> fieldMap = new HashMap<>();for (Field field : clazz.getDeclaredFields()) {if (field.isAnnotationPresent(ExcelIgnore.class)) continue;ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);String key = (annotation != null && !annotation.value().isEmpty()) ? annotation.value() : field.getName();fieldMap.put(key, field);}return fieldMap;}// 根据列名查找字段private static Field findFieldByColumnName(Map<String, Field> fieldMap, String columnName) {// 1. 精确匹配if (fieldMap.containsKey(columnName)) {return fieldMap.get(columnName);}// 2. 忽略大小写匹配for (String key : fieldMap.keySet()) {if (key.equalsIgnoreCase(columnName)) {return fieldMap.get(key);}}return null;}// 获取日期格式(如果有注解指定)private static String getDateFormat(Field field) {ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);return (annotation != null) ? annotation.dateFormat() : "yyyy-MM-dd HH:mm:ss";}// 解析单元格值private static Object parseCellValue(Cell cell, Class<?> targetType, String dateFormat) {switch (cell.getCellType()) {case STRING:return convertStringValue(cell.getStringCellValue().trim(), targetType, dateFormat);case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {return convertDateValue(cell.getDateCellValue(), targetType);} else {return convertNumericValue(cell.getNumericCellValue(), targetType);}case BOOLEAN:return cell.getBooleanCellValue();case FORMULA:return parseFormulaCell(cell, targetType, dateFormat);default:return null;}}// 处理公式单元格private static Object parseFormulaCell(Cell cell, Class<?> targetType, String dateFormat) {try {switch (cell.getCachedFormulaResultType()) {case STRING:return convertStringValue(cell.getStringCellValue().trim(), targetType, dateFormat);case NUMERIC:if (DateUtil.isCellDateFormatted(cell)) {return convertDateValue(cell.getDateCellValue(), targetType);} else {return convertNumericValue(cell.getNumericCellValue(), targetType);}case BOOLEAN:return cell.getBooleanCellValue();default:return null;}} catch (Exception e) {return null;}}// 字符串类型转换private static Object convertStringValue(String value, Class<?> targetType, String dateFormat) {if (value.isEmpty()) return null;try {if (targetType == String.class) return value;if (targetType == Integer.class || targetType == int.class) return Integer.parseInt(value);if (targetType == Long.class || targetType == long.class) return Long.parseLong(value);if (targetType == Double.class || targetType == double.class) return Double.parseDouble(value);if (targetType == Boolean.class || targetType == boolean.class) {return "是".equals(value) || "YES".equalsIgnoreCase(value) || "TRUE".equalsIgnoreCase(value) || "1".equals(value);}if (targetType == LocalDate.class) {return LocalDate.parse(value, DateTimeFormatter.ofPattern(dateFormat));}if (targetType == LocalDateTime.class) {return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(dateFormat));}} catch (Exception e) {throw new IllegalArgumentException("值转换失败: " + value + " -> " + targetType.getSimpleName());}return value;}// 数值类型转换private static Object convertNumericValue(double value, Class<?> targetType) {if (targetType == Integer.class || targetType == int.class) return (int) value;if (targetType == Long.class || targetType == long.class) return (long) value;if (targetType == Double.class || targetType == double.class) return value;if (targetType == Float.class || targetType == float.class) return (float) value;if (targetType == Boolean.class || targetType == boolean.class) return value > 0;return value;}// 日期类型转换private static Object convertDateValue(Date date, Class<?> targetType) {if (date == null) return null;if (targetType == Date.class) return date;if (targetType == LocalDateTime.class) {return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();}if (targetType == LocalDate.class) {return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();}return date;}// 字段映射辅助类private static class FieldMapping {final Field field;final String dateFormat;FieldMapping(Field field, String dateFormat) {this.field = field;this.dateFormat = dateFormat;}}
}

3.实体类示例(带日期格式)

public class User {@ExcelProperty("用户ID")private Long id;@ExcelProperty("用户名")private String username;@ExcelProperty(value = "注册日期", dateFormat = "yyyy-MM-dd")private LocalDate registerDate;@ExcelProperty(value = "最后登录时间", dateFormat = "yyyy/MM/dd HH:mm")private LocalDateTime lastLogin;@ExcelProperty("账户状态")private Boolean active;@ExcelIgnoreprivate String password;// 构造方法/getters/setters
}

4.创建导入控制器

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.util.List;@RestController
@RequestMapping("/import")
public class ImportController {@PostMapping("/users")public List<User> importUsers(@RequestParam("file") MultipartFile file) throws IOException {if (file.isEmpty()) {throw new IllegalArgumentException("请选择上传文件");}// 检查文件类型String fileName = file.getOriginalFilename();if (fileName == null || !fileName.toLowerCase().endsWith(".xlsx")) {throw new IllegalArgumentException("仅支持.xlsx格式文件");}return ExcelImportUtil.importExcel(file, User.class);}// 通用导入接口(适用于任何实体类)@PostMapping("/data")public <T> List<T> importData(@RequestParam("file") MultipartFile file,@RequestParam("className") String className) throws Exception {Class<T> clazz = (Class<T>) Class.forName(className);return ExcelImportUtil.importExcel(file, clazz);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87080.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87080.shtml
英文地址,请注明出处:http://en.pswp.cn/web/87080.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Games101 Lecture3,Lecture4

旋转矩阵逻辑推导 齐次坐标&#xff0c;解决平移的特殊情况 引入一个维度&#xff08;无物理意义&#xff1f;&#xff09;&#xff0c;辅助表达平移&#xff0c;为零时&#xff0c;表示向量&#xff0c;不为零时&#xff0c;表示点&#xff08;/w&#xff09; 三维旋转矩阵 相…

折线图多数据处理

前言&#xff1a; skline1有年份和新申请单位数&#xff0c;skline2有年份和有效期内单位数&#xff0c;我想要把1和2的年份放在一起从小到大放&#xff0c;没有重复的&#xff0c;新申请单位数和有效期内单位数和年份的排列顺序一致 实现&#xff1a; // 获取原始数据 List…

documents4j导出pdf

一、前言 上一篇我们介绍了导出word&#xff0c;既然有了导出word&#xff0c;那么到处pdf也将会出现&#xff0c;导出word和pdf基本上是配套的需求&#xff0c;跑不了&#xff0c;那么本次我就简单介绍一下导出pdf。 二、代码实现 2.1、依赖引入 导出pdf是基于documents4j实现…

从零到一体验 Qwen-TTS:用四川话合成语音的全流程技术实录

今天很高兴看到Qwen-TTS开源。试一试四川方言&#xff08;大概是成都版&#xff09;效果如何。本人无法判断、有兴趣的伙伴可以帮忙听一听。 四川方言TTS "胖娃胖嘟嘟&#xff0c;骑马上成都&#xff0c;成都又好耍。胖娃骑白马&#xff0c;白马跳得高。胖娃耍关刀&…

php数据导出pdf文件

一.导出pdf文件&#xff0c;首先要安装相关的类库文件&#xff0c;我用的是dompdf类库。 1.安装类库文件&#xff1a; composer require dompdf/dompdf 2.引入类库文件到你的控制器中&#xff0c;创建方法&#xff1a; public function generatePdf(){//你需要打印的查询内容…

Beam2.61.0版本消费kafka重复问题排查

1.问题出现过程 在测试环境测试flink的job的任务消费kafka的情况&#xff0c;通过往job任务发送一条消息&#xff0c;然后flink web ui上消费出现了两条。然后通过重启JobManager和TaskManager后&#xff0c;任务从checkpoint恢复后就会出现重复消费。当任务不从checkpoint恢复…

关于 java:9. Java 网络编程

一、Socket 编程 Socket&#xff08;套接字&#xff09;是网络通信的端点&#xff0c;是对 TCP/IP 协议的编程抽象&#xff0c;用于实现两台主机间的数据交换。 通俗来说&#xff1a; 可以把 Socket 理解为“电话插口”&#xff0c;插上后客户端和服务端才能“通话”。 Sock…

主流零信任安全产品深度介绍

腾讯 iOA 零信任安全管理系统 功能&#xff1a;提供零信任接入、终端安全、数据防泄密等十余种功能模块。可实现基于身份的动态访问控制、终端安全一体化防护、数据防泄密体系等。核心优势&#xff1a;基于腾讯内部千万级终端实践打磨&#xff0c;沉淀丰富场景方案&#xff0c…

LabVIEW装配车体挠度无线测量

针对轨道交通车辆装配过程中车体挠度测量需求&#xff0c;基于LabVIEW开发无线快速测量系统&#xff0c;采用品牌硬件构建高精度数据采集与传输架构。系统通过 ZigBee 无线传输技术、高精度模数转换模块及激光位移传感器&#xff0c;实现装配车体挠度的实时、自动、非接触测量&…

java微服务-linux单机CPU接近100%优化

你这个场景&#xff1a; 4核16G 机器 同时运行了 8个 Spring Boot 微服务&#xff0c;每个 JAR 文件 100多 MB 导致 CPU 接近100% 确实是一个常见但资源紧绷的部署情境。下面是分层的优化建议&#xff0c;包括 JVM、系统、服务架构等多个方面&#xff0c;帮助你 降 CPU、稳…

MySQL表的约束和基本查询

一.表的约束 1.1空属性 当我们填写问卷的时候,经常会有不允许为空的问题,比如电话号,姓名等等.而mysql上我们可以在创建表的时候,如果想要某一列不允许为空,可以加上not null来加以限制: mysql> create table myclass( -> class_name varchar(20) not null, -> cla…

VBA代码解决方案第二十六讲:如何新建EXCEL工作簿文件

《VBA代码解决方案》(版权10028096)这套教程是我最早推出的教程&#xff0c;目前已经是第三版修订了。这套教程定位于入门后的提高&#xff0c;在学习这套教程过程中&#xff0c;侧重点是要理解及掌握我的“积木编程”思想。要灵活运用教程中的实例像搭积木一样把自己喜欢的代码…

【unity游戏开发——网络】套接字Socket的重要API

注意&#xff1a;考虑到热更新的内容比较多&#xff0c;我将热更新的内容分开&#xff0c;并全部整合放在【unity游戏开发——网络】专栏里&#xff0c;感兴趣的小伙伴可以前往逐一查看学习。 文章目录 1、Socket套接字的作用2、Socket类型与创建3、核心属性速查表4、关键方法指…

计算机网络(二)应用层HTTP协议

目录 1、HTTP概念 ​编辑2、工作流程​​ 3、HTTP vs HTTPS​​ 4、HTTP请求特征总结​ 5、持久性和非持久性连接 非持久连接&#xff08;HTTP/1.0&#xff09;​​ ​​持久连接&#xff08;HTTP/1.1&#xff09;​​ 1、HTTP概念 HTTP&#xff08;HyperText Transfer …

c# IO密集型与CPU密集型任务详解,以及在异步编程中的使用示例

文章目录 IO密集型与CPU密集型任务详解&#xff08;C#示例&#xff09;一、基本概念1. IO密集型任务2. CPU密集型任务 二、C#示例1. IO密集型示例1.1 文件操作异步示例1.2 网络请求异步示例1.3 数据库操作异步示例 2. CPU密集型示例2.1 基本CPU密集型异步处理2.2 并行处理CPU密…

用lines_gauss的width属性提取缺陷

自己做了一个图&#xff0c;这个图放在资源里了 结果图是这样&#xff08;这里只结算了窄区&#xff09; 代码和备注如下 read_image (Image11, C:/Users/Administrator/Desktop/分享/15/11.png) rgb1_to_gray (Image11, GrayImage) invert_image (GrayImage, ImageInvert) thr…

从0到100:房产中介小程序开发笔记(中)

背景调研 为中介带来诸多优势&#xff0c;能借助它打造专属小程序&#xff0c;方便及时更新核实租赁信息&#xff0c;确保信息准确无误&#xff0c;像房屋的大致地址、租金数额、租赁条件、房源优缺点等关键信息都能清晰呈现。还可上传房屋拍摄照片&#xff0c;这样用户能提前…

【AI 时代的网络爬虫新形态与防护思路研究】

网络爬虫原理与攻击防护的深度研究报告 网络爬虫技术已进入AI驱动的4.0时代&#xff0c;全球自动化请求流量占比突破51%&#xff0c;传统防御手段在面对高度仿真的AI爬虫时已显疲态。基于2025年最新数据&#xff0c;深入剖析网络爬虫的基本原理、工作流程、分类与攻击方式&…

低代码平台架构设计与关键组件

低代码平台的架构设计是其核心能力的关键支撑&#xff0c;需要平衡可视化开发的便捷性、生成应用的健壮性与性能、可扩展性以及企业级需求&#xff08;如安全、多租户、集成&#xff09;。以下是一个典型的企业级低代码平台架构概览及其关键组件&#xff1a; https://example.…

电商 ERP 系统集成接口指南

电商 ERP 系统的高效运行依赖于与多个业务系统的无缝对接&#xff0c;需要集成的核心接口包括&#xff1a;商品管理、订单处理、库存同步、物流配送、客户管理、财务结算等。这些接口是实现数据互通、业务协同的关键桥梁。 一、电商 ERP 系统集成所需接口类型 &#xff08;一…