java在项目中实现excel导入导出

一、初识EasyExcel*

1. Apache POI

先说POI,有过报表导入导出经验的同学,应该听过或者使用。

Apache POI是Apache软件基金会的开源函式库,提供跨平台的Java API实现Microsoft Office格式档案读写。但是存在如下一些问题:

1.1 学习使用成本较高

对POI有过深入了解的才知道原来POI还有SAX模式(Dom解析模式)。但SAX模式相对比较复杂,excel有03和07两种版本,两个版本数据存储方式截然不同,sax解析方式也各不一样。

想要了解清楚这两种解析方式,才去写代码测试,估计两天时间是需要的。再加上即使解析完,要转换到自己业务模型还要很多繁琐的代码。总体下来感觉至少需要三天,由于代码复杂,后续维护成本巨大。

POI的SAX模式的API可以一定程度的解决一些内存溢出的问题,但是POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大,一个3M的Excel用POI的SAX解析,依然需要100M左右内存。

1.2 POI的内存消耗较大

大部分使用POI都是使用他的userModel模式。userModel的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,相对比较好理解。然而userModel模式最大的问题是在于非常大的内存消耗,一个几兆的文件解析要用掉上百兆的内存。现在很多应用采用这种模式,之所以还正常在跑一定是并发不大,并发上来后一定会OOM或者频繁的full gc。

总体上来说,简单写法重度依赖内存,复杂写法学习成本高。

特点
  1. 功能强大

  2. 代码书写冗余繁杂

  3. 读写大文件耗费内存较大,容易OOM

2. EasyExcel

2.1 重写了POI对07版Excel的解析
  • EasyExcel重写了POI对07版Excel的解析,可以把内存消耗从100M左右降低到10M以内,并且再大的Excel不会出现内存溢出,03版仍依赖POI的SAX模式。

  • 下图为64M内存1分钟内读取75M(46W行25列)的Excel(当然还有急速模式能更快,但是内存占用会在100M多一点)

img

  • 在上层做了模型转换的封装,让使用者更加简单方便
特点
  1. 在数据模型层面进行了封装,使用简单
  2. 重写了07版本的Excel的解析代码,降低内存消耗,能有效避免OOM
  3. 只能操作Excel
  4. 不能读取图片

二、快速入门–QuickStart

0、导入依赖坐标

<!-- EasyExcel -->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.6</version>
</dependency>
<!-- lombok 优雅编程 -->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.20</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId>
</dependency>

导入easyexcel-2.1.6坐标的时候,已依赖传递导入poi-3.17的POI。

1、最简单的读

1.1、需求、准备工作
/*** 需求:单实体导入(从磁盘将文件导入到应用程序)* 导入Excel学员信息到系统。* 包含如下列:姓名、性别、出生日期* 模板详见:学员信息.xlsx*/

学员信息.xlsx

image-20230618174203589

1.2、编写导出数据的实体类
package com.taotie.test;import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Date;/*** --- 天道酬勤 ---** @author QiuShiju* @desc*/
// 基于lombok
@Data
@NoArgsConstructor
@AllArgsConstructor
@HeadRowHeight(20) // 指定列头行高
@ColumnWidth(20) // 指定列宽
public class Student {/*** 学生姓名*/@ExcelProperty(value = "学生姓名", index = 0)private String name;/*** 学生性别*/@ExcelProperty(value = "学生性别", index = 2)private String gender;/*** 学生出生日期*/@ExcelProperty(value = "学生出生日期", index = 1)private Date birthday;/*** id*/// @ExcelProperty(value = "编号",index = 3)@ExcelIgnore // 忽略,不读取private String id;
}

注解: 文章后面有详解

1.3、 读取Excel文件(上传)

调用EasyExcelAPI读取的Excel文件的测试类StudentReadDemo

package com.taotie.test;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;/*** --- 天道酬勤 ---** @author QiuShiju* @desc*/
@SpringBootTest 
public class TestEasyExcel {/*** 测试读取数据*/@Testpublic void testRead() {// 读取文件,读取完之后会自动关闭/*** 参数1:pathName  文件路径;"d:\\学员信息.xls"* 参数2:head		 每行数据对应的实体;Student.class* 参数3:readListener	读监听器,每读一样就会调用一次该监听器的invoke方法* 参数4:sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字,不传默认为0*/// // 封装工作簿对象// ExcelReaderBuilder workBook = EasyExcel.read//         ("E:\\学员信息.xlsx",//                 Student.class,//                 new StudentReadListener( ));// // 封装工作表// ExcelReaderSheetBuilder sheet1 = workBook.sheet( );// // 读取// sheet1.doRead( );// 最简单的写法EasyExcel.read("E:\\学员信息.xlsx",Student.class,new StudentReadListener()).sheet().doRead();}
}

读取Excel的监听器,用于处理读取产生的数据

package com.taotie.listener;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.taotie.domain.Student;/*** @Author Vsunks.v* @Description:*/
public class StudentReadListener extends AnalysisEventListener<Student> {// 每读一行,会调用该invoke方法一次@Overridepublic void invoke(Student data, AnalysisContext context) {System.out.println("data = " + data);}// 全部读完之后,会调用该方法@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// TODO......}
}

2、最简单的写(导出)

2.1 需求、准备工作
/*** 需求:单实体导出(从应用程序将文件导入到磁盘)* 导出多个学生对象到Excel表格* 包含如下列:姓名、性别、出生日期* 模板详见:学员信息.xlsx*/
2.2、编写导出数据的实体
// 还是上个实体类...
2.3、 准备数据并写入到文件
    /*** 测试写出数据*/@Testpublic void simpleWrite() {// 创造数据ArrayList<Student> list = new ArrayList<>( );list.add(new Student("张三","男",new Date(),"1001"));list.add(new Student("李四","女",new Date(),"1002"));list.add(new Student("王五","男",new Date(),"1003"));list.add(new Student("赵六","女",new Date(),"1004"));list.add(new Student("周期","男",new Date(),"1005"));list.add(new Student("茅十八","女",new Date(),"1006"));// 写出的文件路径,当前项目下String fileName = "student.xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, Student.class).sheet("学生信息").doWrite(list);System.out.println("导出OK" );}

image-20230618182323185

image-20230618182345134

三、vue文件上传和下载[重点]

基于springboot的文件上传和下载

0. 导入依赖

<!-- EasyExcel --><!-- lombok --><!-- junit -->

3.1 文件上传

注意: 本地Excel表格,列要和数据库一致

需求: 批量插入计量单位(导入excel数据到项目中)

思路:

  • 前端设计文件上传组件 ,点击开始文件上传
  • 后端接收文件,使用工具解析数据
  • 插入数据库

3.1.1 前端

在页面设计上传的组件

1.设置上传按钮,显示上传的对话框

2.设置对话框+上传组件

image-20240726113742898

  <!-- 上传excel的对话框 --><el-dialog title="上传计量单位Excel" :visible.sync="dialogExcelVisible" width="40%"><el-uploadclass="upload-demo"dragaction="http://localhost:8888/md/unit/upload/excel"accept=".xlsx,.xls":on-success="uploadExcelSuccess":on-error="uploadExcelError"multiple><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div><div class="el-upload__tip" slot="tip">只能上传.xlsx,.xls文件,且不超过500kb</div></el-upload></el-dialog>
uploadExcelSuccess(){this.$message({type:"success",message:"上传成功"})this.dialogExcelVisible = false;this.fetchData();
},
uploadExcelError(err){this.$message({type:"error",message:err})
}
3.1.2 后端-实体类注解

image-20240726113906412

3.1.3 后端-监听器
package com.qf.config;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.qf.entity.SysUser;
import com.qf.entity.UnitMeasure;
import com.qf.service.SysUserService;
import com.qf.service.UnitMeasureService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;import java.util.ArrayList;/*** --- 天道酬勤 ---** @author QiuShiju* @desc*/
@Component
@Scope("prototype")    // 作者要求每次读取都要使用新的Listener
public class MdUnitMeasureReadListener extends AnalysisEventListener<UnitMeasure> {// 有个很重要的点 DemoDataListener 不能被spring管理,// 要每次读取excel都要new,然后里面用到spring可以构造方法传进去private UnitMeasureService service;public MdUnitMeasureReadListener(){} // 空参构造// 有参构造public MdUnitMeasureReadListener(UnitMeasureService service){this.service = service;}// 每隔5条存储数据库,实际项目中使用时可以100条,然后清理list ,方便内存回收private final int BATCH_COUNT = 5;private ArrayList<UnitMeasure> list = new ArrayList<>( );// 每读一行,会调用该invoke方法一次@Overridepublic void invoke(UnitMeasure data, AnalysisContext context) {list.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (list.size( ) >= BATCH_COUNT) {// 调用方法,执行插入saveData( );// 存储完成清理 listlist.clear();}}// 全部读完之后,会调用该方法@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库if(list.size() > 0) {saveData( );}System.out.println("所有数据解析完成!");}// 向数据库插入数据private void saveData() {System.out.println("开始存储数据库!");// 调用业务层存数据库service.saveBatch(list);System.out.println("存储数据库成功!");}
}
3.1.4 后端-Controller接收
 /*** 上传Excel文件*/@PostMapping("/upload/excel")public R uploadExcel(MultipartFile file) {try {// 重点,此处第三个参数需要传入Service对象EasyExcel.read(file.getInputStream( ),UnitMeasure.class,new MdUnitMeasureReadListener(service)).sheet( ).doRead( );}catch (Exception e){e.printStackTrace();return R.fail(e.getMessage());}return R.ok( );}
3.1.5 后端-Service
// 需要添加批量插入的方法
3.1.6 后端-Mapper
       <!-- 批量插入 --><insert id="saveBatch">insert intomd_unit_measure       (measure_code,measure_name,primary_flag,primary_id,change_rate,enable_flag)values<foreach collection="list" item="unit" separator=",">(#{unit.measureCode},#{unit.measureName},#{unit.primaryFlag},#{unit.primaryId},#{unit.changeRate},#{unit.enableFlag})</foreach>
3.1.7 测试

本地创建一个excel表格,按照实体类中定义的列名填充数据

点击上传

image-20231220105443796

3.2 文件导出

需求: 将项目中的数据导出到本地excel表格

思路:

  • 前端设计按钮,点击开始导出
  • 后端请求,查询出数据,
  • 使用工具导出

实体类于之前一样

// 略

Controller层

/*** 下载Excel文件* 注意,返回值是void*/
@GetMapping("/download/excel")
public void downloadExcel(HttpServletResponse response) throws IOException {//  查询数据库全部数据List<UnitMeasure> list = service.findAll(null);// 下面这个注释是前端使用方式1,即a标签发请求时采用response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止中文乱码String fileName = URLEncoder.encode("计量单位信息", "UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");// easyexcel导出工具EasyExcel.write(response.getOutputStream(), UnitMeasure.class).autoCloseStream(Boolean.FALSE).sheet("计量单位信息").doWrite(list);
}

// todo servicce+mapper实现查询全部

前端

1.导出按钮触发函数

image-20240726145525475

2.函数内确认导出,发出请求到后端

image-20240726145608991

3.3、自定义单元格样式

EasyExcel支持调整行高、列宽、背景色、字体大小等内容,但是控制方式与使用原生POI无异,比较繁琐,不建议使用。

但是可以使用模板填充的方式,向预设样式的表格中直接写入数据,写入数据的时候会保持原有样式。

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

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

相关文章

SpringBoot 项目——抽奖系统

本项目主要实现的功能是&#xff1a;主要服务于管理员用户&#xff0c;其可圈选奖品&#xff0c;人员来创建抽奖活动&#xff0c;并进行在线抽奖&#xff0c;并可通过短信或邮件的方式通知中奖者&#xff0c;同时普通用户可查看已结束的抽奖活动的中奖结果&#xff1b; 一、项…

TESSY创建单元测试或集成测试工程

我们以tessy5.1 IDE为例&#xff0c;给大家展示工程的创建过程。 1、打开TESSY5.1软件后&#xff0c;会弹出&#xff1a; 2、点击NEW Project后&#xff0c;会弹出&#xff1a; 3、接下来&#xff0c;就可以打开刚创建的工程&#xff1a; 4、进入到TESSY的主界面后&#xff0c…

Python办公自动化 获取文本数据 支持多种类型文件

学好办公自动化,走遍天下都不怕&#xff01;&#xff01; 前面我们已经学习了&#xff0c;如何用python的下载安装以及入门基础知识&#xff0c;并且也知道如何使用python自动处理Excel文件数据、如何批量生成Word文件、如何对数据分析后生成洞察报告、如何用python实现自动发送…

React中实现antd自定义图标,鼠标悬浮变色

借助 antd 的 tooltip 组件来实现 hover 时变色的效果 1.新建组件 自定义图标一般在iconfont上面获取&#xff0c;复制下来的svg代码&#xff0c;切记要删除 fill 属性后添加到组件中 import { Tooltip } from "antd"; import React from "react";const …

webpack4手动搭建Vue项目

小满视频 很多解释使用通义灵码搜的,通义灵码的搜索结果也是有错误的全程使用pnpm包管理工具&#xff0c;和npm的用法基本一样 学习总结 1. 多看看webpack官网 2. webpack的作用&#xff1a;配置一堆东西&#xff0c;达到运行程序的目的 3. 无论什么东西都转成js&#xff0c;…

理解数据库系统的内部结构

数据库系统在我们的数字世界中扮演着关键角色。本文将介绍数据库系统的内部结构&#xff0c;帮助初学者了解其基本概念。 数据库系统的三级模式 数据库系统内部采用三级模式二级映像结构&#xff0c;包括外模式、模式和内模式。这种结构确保了数据的逻辑独立性和物理独立性。…

如何在Android项目中进行性能优化分析?

引言 在开发过程中&#xff0c;性能优化是一个重要的话题。用户对于应用的性能有着非常高的期望&#xff1a;快速启动、流畅的操作体验、低内存消耗等。那么&#xff0c;作为开发者&#xff0c;我们如何才能在项目中进行性能优化分析呢&#xff1f;今天就来和大家聊聊这个话题…

Linux常见基础命令

Linux基础 初级学习阶段需要了解的知识一、Linux基础命令查阅命令帮助信息1.man2.help Linux命令的基本实用目录操作文件内容操作查看某文件下的用户操作日志压缩和解压缩sudo用户权限操作用户权限操作TOP文件安装 上一篇 VMware安装linux环境 初级学习阶段需要了解的知识 1.…

什么是 AWS CloudWatch?

AWS CloudWatch 是 AWS 提供的一项全面的监控和可观测性服务&#xff0c;使用户能够收集和可视化指标、日志和事件&#xff1b;设置警报&#xff1b;并根据预定义的条件自动执行操作。CloudWatch 提供对 AWS 资源和应用程序的运行状况、性能和运行状态的深入了解&#xff0c;使…

autoware整体架构的分析

autoware framework sensinglidar driver&#xff08;lidar驱动&#xff09;PointCloud Preprocessing&#xff08;点云预处理&#xff09;Detection&#xff08;检测&#xff09;GNSS (全球导航卫星系统)IMU (惯性测量单元) Localization&#xff08;定位&#xff09;Pose Ini…

#Datawhale X 李宏毅苹果书 AI夏令营#1.2了解线性模型

1.2线性模型 什么是线性模型&#xff1f; 初始模型&#xff1a;, 其中y表示观看人数&#xff0c;x1表示前一天的观看人数&#xff0c;这个模型就是在用前一天的观看人数来预测当前的观看人数。 模型改进&#xff1a; 然而真实的数据是有周期性的&#xff0c;每隔7天&#…

这本大模型书一定要读!李开复、周鸿祎都强烈推荐的《实战AI大模型》(附PDF)

《实战AI大模型》是一本旨在填补人工智能&#xff08;AI&#xff09;领域&#xff08;特别是AI大模型&#xff09;理论与实践之间鸿沟的实用手册。书中介绍了AI大模型的基础知识和关键技术&#xff0c;如Transformer、BERT、ALBERT、T5、GPT系列、InstructGPT、ChatGPT、GPT 4、…

无人机之云台的作用

无人机云台在无人机技术中扮演着至关重要的角色&#xff0c;其作用主要体现在以下几个方面&#xff1a; 一、 确保拍摄稳定性 防抖动&#xff1a;无人机在飞行过程中&#xff0c;尤其是在复杂环境下&#xff0c;如遇到风力干扰或进行高速飞行时&#xff0c;机身容易产生震动和…

Prometheus+Grafana的安装和入门

概念 什么是Prometheus? Prometheus受启发于Google的Brogmon监控系统&#xff08;相似kubernetes是从Brog系统演变而来&#xff09;&#xff0c; 从2012年开始由google工程师Soundclouds使用Go语言开发的开源监控报警系统和时序列数据库(TSDB)。&#xff0c;并且与2015年早起…

【Linux —— POSIX信号量 - 基于环形队列的生产消费模型】

Linux —— POSIX信号量 - 基于环形队列的生产消费模型 POSIX信号量信号量的概念POSIX信号量的类型信号量的操作 POSIX信号量函数基于环形队列的生产消费模型设计思路同步和安全性代码 POSIX信号量 信号量的概念 POSIX信号量是一种用于进程和线程之间同步的机制&#xff0c;主…

【netty系列-08】深入Netty组件底层原理和基本实现

Netty系列整体栏目 内容链接地址【一】深入理解网络通信基本原理和tcp/ip协议https://zhenghuisheng.blog.csdn.net/article/details/136359640【二】深入理解Socket本质和BIOhttps://zhenghuisheng.blog.csdn.net/article/details/136549478【三】深入理解NIO的基本原理和底层…

数据结构(邓俊辉)学习笔记】串 16——Karp-Rabin算法:串即是数

文章目录 1. 化串为数2. 凡物皆数3. 亦是数 1. 化串为数 接下来的这节&#xff0c;我们再来讨论一种十分另类的串匹配算法&#xff0c;也就是所谓的 Karp-Rabin 算法。回顾此前所介绍的几种串匹配算法&#xff0c;我们所面临的难题是一样的。也就是说在这里&#xff0c;我们每次…

Windows 10/11降级漏洞的工具包现已发布 仅供安全测试

早前有研究人员在分析 Windows 10/11 更新机制时发现微软虽然已经考虑到潜在的安全问题增加了各种限制&#xff0c;但还是存在失误因此存在弱点&#xff0c;研究人员则通过该弱点成功降级了系统。通过该漏洞不仅可以成功降级系统&#xff0c;同时系统还会认为自己已经完成更新并…

Java面试题·区别题·JavaSE部分

系列文章目录 总章 Java区别题 文章目录 系列文章目录前言private/默认/protected/public权限修饰符的区别&和&&区别和联系&#xff0c;I和II区别和联系if和switch的不同之处和equals的区别和联系数组做形参和可变参数做形参联系和区别接口和抽象类的异同之处面向…

嵌入式day36

数据库 专业存储数据、大量数据 数组、链表、变量---->内存&#xff1a;程序运行结束、掉电数据丢失 文件---->硬盘&#xff1a;程序运行结束、掉电数据不丢失 数据库---->硬盘 数据库文件与普通文件区别&#xff1a; 1.普通文件对数据管理&#xff08;增删改查…