当前位置: 首页 > article >正文

SpringBoot 数据库批量导入导出 Xlsx文件的导入与导出 全量导出 数据库导出表格 数据处理 外部数据

介绍

poi-ooxml 是 Apache POI 项目中的一个库,专门用于处理 Microsoft Office 2007 及以后版本的文件,特别是 Excel 文件(.xlsx 格式)和 Word 文件(.docx 格式)。

在管理系统中需要对数据库的数据进行导入或导出在系统中经常使用的到。

Java针对MS Office的操作的库屈指可数,比较有名的就是Apache的POI库。这个库异常强大,但是使用起来也并不容易。Hutool针对POI封装一些常用工具,使Java操作Excel等文件变得异常简单。

胡图官网:https://doc.hutool.cn/pages/poi/#%E7%94%B1%E6%9D%A5

Hutool-poi是针对Apache POI的封装,因此需要用户自行引入POI库,Hutool默认不引入。

  • XLS:是 Excel 97-2003 版本的文件格式,基于二进制文件格式。存储数据和工作表信息采用了二进制格式,不易直接读取和处理。

  • XLSX:是 Excel 2007 及更高版本使用的文件格式,基于 XML。XLSX 实际上是一个压缩的文件包,里面包含了多个 XML 文件,更加标准化和易于扩展。

依赖

<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version>
</dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.18</version>
</dependency>

实体类

支持别名注解的。可以在字段上加@Alias注解。

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("site")
public class Site implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)@Alias(value = "编号")private Integer id;@TableField("ip_address")@Alias(value = "IP地址")private String ipAddress;@TableField("source")@Alias(value = "来源")private String source;@TableField("operation")private String operation;@TableField("access_time")private LocalDateTime accessTime;}

在这里插入图片描述

导出Xls

 @GetMapping()public void export(HttpServletResponse response) throws Exception{String fileName = "测试.xls";String encodedFileName = URLEncoder.encode(fileName, "UTF-8");encodedFileName = encodedFileName.replace("+", "%20");  // 处理空格字符List<Site> list = siteService.list();
// 通过工具类创建writer,默认创建xls格式ExcelWriter writer = ExcelUtil.getWriter();// 一次性写出内容,使用默认样式,强制输出标题writer.write(list, true);//response为HttpServletResponse对象response.setContentType("application/vnd.ms-excel;charset=utf-8");//test.xls是弹出下载对话框的文件名response.setHeader("Content-Disposition","attachment;filename="+encodedFileName);ServletOutputStream out=response.getOutputStream();writer.flush(out, true);// 关闭writer,释放内存writer.close();
//此处记得关闭输出Servlet流IoUtil.close(out);}

在这里插入图片描述

导出Xlsx

@GetMapping()
public void export(HttpServletResponse response) throws Exception{String  fileName = URLEncoder.encode("测试.xlsx", "UTF-8").replace("+", "%20");List<Site> list = siteService.list();ExcelWriter writer = ExcelUtil.getWriter(true);writer.write(list, true);response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");response.setHeader("Content-Disposition","attachment;filename="+fileName);ServletOutputStream out=response.getOutputStream();writer.flush(out, true);writer.close();IoUtil.close(out);}

以上都是基于全量数据的导出方法,下面是按需导出。


创建自定义注解

@Retention(RetentionPolicy.RUNTIME) // 保证注解在运行时可访问
public  @interface ExcelTitle {String value(); // 用来保存 Excel 列标题
}

自定义实体类

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("site")
public class Site implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)private Integer id;@TableField("ip_address")@ExcelTitle("IP地址")private String ipAddress;@TableField("source")@ExcelTitle("来源")private String source;@TableField("operation")@ExcelTitle("测试字段")private String operation;@TableField("access_time")@ExcelTitle("访问时间")private LocalDateTime accessTime;}

按需不排序

@GetMapping() 
public void export(HttpServletResponse response) throws Exception{// 需要导出的字段名String[] key = {"访问时间", "IP地址", "来源"};// 查找性能较高Set<String> keySet = new HashSet<>(Arrays.asList(key));// 对文件名进行 URL 编码,防止中文字符出现乱码,并将 "+" 替换成 "%20"String fileName = URLEncoder.encode("测试.xlsx", "UTF-8").replace("+", "%20");// 从服务层获取数据列表,通常是从数据库或其他数据源获取List<Site> list = siteService.list();// 创建一个 ExcelWriter 实例,用于写入 Excel 文件,true 表示创建时有表头ExcelWriter writer = ExcelUtil.getWriter(true);// 获取 Site 类的所有字段(反射机制)Field[] fields = Site.class.getDeclaredFields();// 遍历每个字段for (Field field : fields) {// 获取字段上是否有 @ExcelTitle 注解ExcelTitle excelAnnotation = field.getAnnotation(ExcelTitle.class);// 如果字段上存在 @ExcelTitle 注解if (excelAnnotation != null) {// 获取注解中的 value 值(即字段对应的 Excel 标题)String excelValue = excelAnnotation.value();// 判断当前字段的 Excel 标题是否是我们关心的字段if (Arrays.asList(key).contains(excelValue)) {// 如果是我们关心的字段,则添加标题别名(即字段名与 Excel 表头的映射关系)writer.addHeaderAlias(field.getName(), excelValue);}}}// 默认情况下,未添加 alias 的属性也会被写出。如果我们只想输出加了别名的字段,可以调用该方法writer.setOnlyAlias(true);// 将数据写入 Excel 文件,true 表示需要写入表头writer.write(list, true);// 设置响应内容类型为 Excel 文件格式response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");// 设置响应头,指定下载的文件名response.setHeader("Content-Disposition", "attachment;filename=" + fileName);// 获取响应输出流,用于将文件写入客户端ServletOutputStream out = response.getOutputStream();// 将 Excel 内容刷新到输出流writer.flush(out, true);// 关闭 writer 释放资源writer.close();// 关闭输出流IoUtil.close(out);
}

按需排序导出

    @GetMapping()public void export(HttpServletResponse response) throws Exception {String[] key = {"访问时间", "IP地址", "来源"};Set<String> keySet = new HashSet<>(Arrays.asList(key));String fileName = URLEncoder.encode("测试.xlsx", "UTF-8").replace("+", "%20");List<Site> list = siteService.list();ExcelWriter writer = ExcelUtil.getWriter(true);for (String k : key) {String title =  getCharacter(Site.class, k);if(title==null)continue;writer.addHeaderAlias(title, k);}//        //自定义标题别名
//        writer.addHeaderAlias("ipAddress", "IP地址");// 默认的,未添加alias的属性也会写出,如果想只写出加了别名的字段,可以调用此方法排除之writer.setOnlyAlias(true);writer.write(list, true);response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");response.setHeader("Content-Disposition", "attachment;filename=" + fileName);ServletOutputStream out = response.getOutputStream();writer.flush(out, true);writer.close();IoUtil.close(out);}
/*** 根据传入的类和关键字,查找类中具有 @ExcelTitle 注解的字段,* 如果该字段的 @ExcelTitle 注解的 value 属性与传入的关键字匹配,则返回该字段的名称。** @param clazz 目标类的 Class 对象,用于获取类的字段信息。* @param key   需要匹配的关键字,用于查找与其对应的 @ExcelTitle 注解的字段。* @return 如果找到匹配的字段,则返回字段名称;否则返回 null。*/
public String getCharacter(Class<Site> clazz, String key) {// 获取目标类的所有字段Field[] fields = clazz.getDeclaredFields();// 遍历所有字段for (Field field : fields) {// 获取当前字段的 @ExcelTitle 注解ExcelTitle excelAnnotation = field.getAnnotation(ExcelTitle.class);// 如果字段具有 @ExcelTitle 注解if (excelAnnotation != null) {// 获取注解的 value 值String excelValue = excelAnnotation.value();// 检查传入的 key 是否与 @ExcelTitle 注解中的值匹配if (Arrays.asList(key).contains(excelValue)) {// 如果匹配,返回字段名称return field.getName();}}}// 如果没有找到匹配的字段,返回 nullreturn null;
}

在这里插入图片描述
也可以将该功能给前端去做。


批量导入

@PostMapping("/import")
public String importData(MultipartFile file) throws IOException {//读取文件转成输入流ExcelReader reader = ExcelUtil.getReader(file.getInputStream());List<Object> header = reader.readRow(0); // 读取第0行作为列标题for (Object column : header) {reader.addHeaderAlias(column+"",getCharacter(Site.class,column+""));}List<Site> list = reader.readAll(Site.class);siteService.saveBatch(list);return null;
}

模板下载

@GetMapping("/template")
public void template(HttpServletResponse response) throws Exception {// 定义模板标题String[] key = {"访问时间", "IP地址", "来源"};// 文件名编码String fileName = URLEncoder.encode("测试.xlsx", "UTF-8").replace("+", "%20");// 创建一个空的 List,作为数据源,模板将只包含标题行List<Map<String, Object>> list = new ArrayList<>();// 生成空数据的行Map<String, Object> emptyRow = new HashMap<>();for (String column : key) {emptyRow.put(column, "");  // 为空字段赋空值}list.add(emptyRow); // 将空数据行添加到 list// 创建 ExcelWriterExcelWriter writer = ExcelUtil.getWriter(true);// 为每一列设置别名for (String k : key) {writer.addHeaderAlias(k, k);  // 直接使用标题作为别名}// 写入空数据(标题和一行空数据)writer.write(list, true);// 设置响应的内容类型和头部,触发浏览器下载response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");response.setHeader("Content-Disposition", "attachment;filename=" + fileName);// 获取输出流,写入到客户端ServletOutputStream out = response.getOutputStream();// 将数据写入输出流writer.flush(out, true);writer.close();IoUtil.close(out);
}

测试demo,实际业务自行优化

http://www.lryc.cn/news/2398811.html

相关文章:

  • 解决:install via Git URL失败的问题
  • OpenCV CUDA模块特征检测------创建Harris角点检测器的GPU实现接口cv::cuda::createHarrisCorner
  • 【氮化镓】钝化层对p-GaN HEMT阈值电压的影响
  • C++:优先级队列
  • 睡眠分期 html
  • Java求职者面试:Spring、Spring Boot、Spring MVC与MyBatis技术深度解析
  • Github 2025-05-29 Go开源项目日报Top9
  • 前端项目种对某个文件夹进行大小写更改,git识别不到差异导致无变化
  • AWS VPC 网络详解:理解云上专属内网的关键要素
  • Ubuntu24.04.2 + kubectl1.33.1 + containerdv1.7.27 + calicov3.30.0
  • 循环神经网络(RNN)全面教程:从原理到实践
  • uniapp 键盘顶起页面问题
  • 利用TOA与最小二乘法直接求解
  • SpringBoot系列之RabbitMQ 实现订单超时未支付自动关闭功能
  • 【C++高级主题】命令空间(五):类、命名空间和作用域
  • ArcGIS Pro 3.4 二次开发 - 地图创作 1
  • 2.1HarmonyOS NEXT开发工具链进阶:DevEco Studio深度实践
  • MyBatis常用注解全解析:从基础CRUD到高级映射
  • 国标GB28181设备管理软件EasyGBS视频平台筑牢文物保护安全防线创新方案
  • 十二、【核心功能篇】测试用例列表与搜索:高效展示和查找海量用例
  • Baklib内容中台AI重构智能服务
  • 数据库包括哪些?关系型数据库是什么意思?
  • Python爬虫监控程序设计思路
  • Edge浏览器怎样开启兼容模式
  • 【HarmonyOS 5】Laya游戏如何鸿蒙构建发布详解
  • C++ TCP传输心跳信息
  • Elasticsearch | 如何将修改已有的索引字段类型并迁移数据
  • c++之STL容器的学习(上)
  • Linux 环境下高效视频切帧的实用指南
  • 【鱼皮-用户中心】笔记