easyExcel使用案例有代码

ops/2025/3/6 13:32:11/

easyExcel_webexcel_0">easyExcel 入门,完成web的excel文件创建和导出

easyExcel官网
在这里插入图片描述

EasyExcel 的主要特点如下:

1、高性能:EasyExcel 采用了异步导入导出的方式,并且底层使用 NIO 技术实现,使得其在导入导出大数据量时的性能非常高效。

2、易于使用:EasyExcel 提供了简单易用的 API,用户可以通过少量的代码即可实现复杂的 Excel 导入导出操作。

3、增强的功能“EasyExcel 支持多种格式的 Excel 文件导入导出,同时还提供了诸如合并单元格、数据校验、自定义样式等增强的功能。

4、可扩展性好:EasyExcel 具有良好的扩展性,用户可以通过自定义 Converter 对自定义类型进行转换,或者通过继承 EasyExcelListener 来自定义监听器实现更加灵活的需求。

入门使用

easyExcel_16">使用maven导入easyExcel坐标

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.0</version>
</dependency>

定义实体类封装数据,一行对应一个对象 用@ExcelProperty注解完成与excel的映射

java">@Data
@AllArgsConstructor
@NoArgsConstructor
public class CategoryExcelVo {// `value` 指定 Excel 表头名称,数据导入导出时匹配该表头// `index`(可选)从 0 开始,表示 Excel 第 n+1 列// 如果省略 `index`,则按字段声明顺序匹配列@ExcelProperty(value = "id" ,index = 0)private Long id;@ExcelProperty(value = "名称" ,index = 1)private String name;
}

excel对应的表
在这里插入图片描述

读入操作 read 读需要用到ExcelListener对象

1、 先创建这个对象

java">@Getter
public class ExcelListener<T> extends AnalysisEventListener<T> {/*** 每读取一行就执行一次* @param t    返回的数据* @param analysisContext*///读取后对象存储在data集合中List<T> data=new ArrayList<>();@Overridepublic void invoke(T t, AnalysisContext analysisContext) {data.add(t);}/***  都读取解析完毕后执行,   一般用于对资源的处理, 或对操作的补充* @param analysisContext*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {System.out.println("表格的解析完成了");}

2、 进行读测试

java">private static void readFile() {// 文件的路径String filePath="/Users/chen/01.xlsx";// easyExcel不能由spring 接管, 这里手动new对象ExcelListener<CategoryExcelVo> excelVoExcelListener = new ExcelListener<>();//读操作EasyExcel.read(filePath, CategoryExcelVo.class,excelVoExcelListener).sheet().doRead();//读取完毕后数据存放到了, 监听器的data里面,遍历取出来就可以List<CategoryExcelVo> data = excelVoExcelListener.getData();for (CategoryExcelVo item : data) {System.out.println(item);}}
写出操作 write 不需要用到ExcelListener对象
java">    private static void writeFile() {//要写到的磁盘路径String filePath="/Users/chen/categoryWrite.xlsx";//创建每一个对象,对应一行数据List<CategoryExcelVo> list = new ArrayList<>() ;list.add(new CategoryExcelVo(1L , "数码办公")) ;list.add(new CategoryExcelVo(11L , "华为手机")) ;//参数    1路径名    2 对应的类     3 excel中sheet的名字       4 集合EasyExcel.write(filePath,CategoryExcelVo.class).sheet("category").doWrite(list);System.out.println("finished write");}

在web中使用

后端

1、controller

java">@RestController
@RequestMapping("/admin/product/category")
public class CategoryController {@Autowiredprivate CategoryService categoryService;/*** 用户上传excel* @param file* @return*/@PostMapping("uploadExcel")public Result uploadExcel(MultipartFile file){categoryService.uploadExcel(file);return Result.build(null,ResultCodeEnum.SUCCESS);}/*** 用户导出excel* @param response*/@GetMapping("/excelExport")//不需要返回值, 由response设置public void excelExport(HttpServletResponse response){categoryService.excelExport(response);}
}

2、service

java">@Service
@Transactional
public class CategoryServiceImpl implements CategoryService {@Autowiredprivate CategoryMapper categoryMapper;/*** 用户上传excel*  1 定义对应的实体类*  2 定义监听器*  3  读取数据*  4  上传到数据库* @param file*/@Overridepublic void uploadExcel(MultipartFile file) {try {//创建监听器CategoryListener<CategoryExcelVo> CategoryListener = new CategoryListener<CategoryExcelVo>(categoryMapper);EasyExcel.read(file.getInputStream(), CategoryExcelVo.class,CategoryListener).sheet().doRead();} catch (IOException e) {e.printStackTrace();}}/***  导出 excel文件,写操作* @param response*/@Overridepublic void excelExport(HttpServletResponse response) {try {// 设置mime类型response.setContentType("application/vnd.ms-excel");//设置浏览器编码方法response.setCharacterEncoding("utf-8");//对路径进行编码String fileName = URLEncoder.encode("分类数据", "UTF-8");//设置 HTTP 响应头,告诉浏览器该请求是一个文件下载(attachment),并指定下载的文件名为 fileName.xlsx。response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");//准备数据//查询全部分类,不需要带树形结构List<Category>  categoryList=categoryMapper.selectAllCategory();//查出来的数据比实际用到的数据多//   查询出来的类和对应的vo类不匹配, 但是有部分属性相同,这时候可以用map()函数List<CategoryExcelVo> categoryExcelVoList = categoryList.stream().map(category -> {CategoryExcelVo categoryExcelVo = new CategoryExcelVo();//   BeanUtils.copyProperties();   可以把参数一的属性的值,拷贝到参数2中,    要求两个属性必须一致BeanUtils.copyProperties(category, categoryExcelVo);return categoryExcelVo;}).collect(Collectors.toList());//写出数据EasyExcel.write(response.getOutputStream(), CategoryExcelVo.class).sheet("category").doWrite(categoryExcelVoList);} catch (Exception e) {e.printStackTrace();}}
}
  • 创建对应的导出实体类的监听器
java">@Slf4j
public class CategoryListener<T> extends AnalysisEventListener<T> {//  没有办法用自动注入的方式,获取mapper, 所以创建对象的时候手动//把参数带过来private CategoryMapper categoryMapper;public CategoryListener(CategoryMapper categoryMapper) {this.categoryMapper = categoryMapper;}/*** 每隔100条存储数据库,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 100;private List<CategoryExcelVo> CategoryList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Overridepublic void invoke(T data, AnalysisContext context) {log.info("解析到一条数据:{}", JSON.toJSONString(data));CategoryList.add((CategoryExcelVo) data);if (CategoryList.size() >= BATCH_COUNT) {//每保存到100条,执行一次mapper的插入方法saveData();CategoryList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}/*** 做兜底,如果最后没有100条数据了,也保证剩余的都保存的数据库中* @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {saveData();log.info("所有数据解析完成!");}/*** 存储到数据库*/private void saveData() {log.info("{}条数据,开始存储数据库!", CategoryList.size());log.info("存储数据库成功!");categoryMapper.insertExcelData(CategoryList);}
}

3、mapper

java">@Mapper
public interface CategoryMapper {//查询所有分类  导出List<Category> selectAllCategory();// 导入excelvoid insertExcelData(List<CategoryExcelVo> categoryList);
}
  • 注意在导入excel的时候, id字段也需要我们自己处理, 否则在网页的父子的层级关系就显示不出来了. 我们是通过id 和pid 来判断层级关系的
    <select id="selectAllCategory" resultType="com.chen.model.entity.product.Category">select <include refid="columns"/> from categorywhere  is_deleted=0order by  order_num</select><insert id="insertExcelData"  >insert into category (id,name,image_url,parent_id,status,order_num)values<foreach collection="categoryList" separator="," item="category">(#{category.id},#{category.name},#{category.imageUrl},#{category.parentId},#{category.status},#{category.orderNum})</foreach></insert>

前端

1、 api

import request from '@/utils/request'
// 抽取出来方便后面使用
const apiUrl = '/admin/product/category'/*** 导出excel文件* @returns {AxiosPromise}*/
export const exportExcel = () => {return request({url: `${apiUrl}/excelExport`,method: 'get',responseType:'blob' //用于处理二进制数据})
}

2、 vue

•	el-upload 组件默认使用 file 作为参数名,即后端 MultipartFile 应该使用 @RequestParam("file") 来接收,如果名字一样可以省略。
<template><div class="tools-div"><el-button type="success" size="small" @click="exportExcelFile">导出</el-button><el-button type="primary" size="small" @click="showImportExcelDialog">导入</el-button></div>
<!--  导入对话框--><el-dialog v-model="dialogImportVisible" title="导入" width="30%"><el-form label-width="120px"><el-form-item label="分类文件"><el-uploadclass="upload-demo"action="http://localhost:8501/admin/product/category/uploadExcel":on-success="onUploadSuccess":headers="headers"><el-button type="primary">上传</el-button></el-upload></el-form-item></el-form></el-dialog>
</template>
```js 
<script setup>javascript">
import {exportExcel} from '@/api/category'
//导出------------
//点击导出后,开始执行方法 本方法是给按钮绑定的
const exportExcelFile = async () => {// exportExcel() 是一个异步函数,返回一个 Promise。//.then((resp => { })) 监听 Promise 的成功状态,但回调函数中没有任何操作。exportExcel().then((resp => {//创建blob对象let blob = new Blob([resp]);//创建a标签const link = document.createElement("a");//赋值数据link.href=window.URL.createObjectURL(blob);//设置名称link.download="分类数据.xlsx"//模拟点击link.click()}))
}
// --------------导入
//  控制对话框是否展示
const dialogImportVisible=ref(false);//点击导入后显示导入对话框
const showImportExcelDialog=()=>{dialogImportVisible.value=true;
}//上传成功的回掉函数, 关闭对话框
const onUploadSuccess=async(resp)=>{const {code}=respif(code===200){dialogImportVisible.value=falseElMessage.success("上传成功")//刷新页面, 查询一下就行//进入后查询一级标题const {data} = await getCategoryListByParentId(0)//赋值list.value = data}
}
</script>

http://www.ppmy.cn/ops/163609.html

相关文章

进阶篇——深入解析数据库事务与锁机制:从原理到实战优化

引言&#xff1a;并发控制的挑战与价值 在电商秒杀场景中&#xff0c;某平台曾因事务控制不当导致超卖事故&#xff0c;直接经济损失达百万级别。这种惨痛教训揭示了事务与锁机制在现代数据库系统中的核心地位。本文将从底层原理到生产实践&#xff0c;全方位剖析事务处理的关…

Linkreate wordpress AI插件,一款文章图文、关键词等自动后台生成的简单、高效、智能、定制化的AI插件

&#x1f680; Linkreate wordpress AI插件核心功能亮点 文章生成与优化 自动化文章生成&#xff1a;利用 AI 技术&#xff0c;根据关键词生成高质量文章。 支持指定长度和要求&#xff0c;异步生成不阻塞操作。 且 AI 可自动生成精准的 tag 标签&#xff0c;利于 SEO 优化…

CRM一张表单开发的思路

在开发CRM项目的这几个星期里&#xff0c;我遇到了不少困难&#xff0c;最大的困难是对开发一张表单的完整流程缺乏清晰的思路。回想起开发第一张表单时&#xff0c;我完全处于照抄的状态。当时&#xff0c;我负责的是正式客户申请单&#xff0c;而泓宇开发的潜在客户申请单和我…

linux上redis升级

linux上redis升级 redis版本升级。 我原本的redis 版本是6.2.1&#xff0c;现在就对他做一下升级处理。 1、下载redis 源码包&#xff1a; redis 下载地址 根据下载地址选择自己要安装的redis 版本的源码包。这里我下载的是 redis-6.2.6.tar.gz。 这里你可以先下载到本地&am…

玩转大模型——Trae AI IDE国内版使用教程

文章目录 Trae AI IDE完备的 IDE 功能强大的 AI 助手 安装 Trae 并完成初始设置管理项目什么是 “工作空间”&#xff1f;创建项目 管理插件安装插件从 Trae 的插件市场安装从 VS Code 的插件市场安装 禁用插件卸载插件插件常见问题暂不支持安装 VS Code 插件市场中某个版本的插…

【零基础C语言】第四节 数组

【零基础C语言系列】 【零基础C语言】第一节 C语言概述【数制进制码制】-CSDN博客 【零基础C语言】第二节 数据类型、运算符、表达式-CSDN博客 【零基础C语言】第三节 控制结构-CSDN博客 一、一维数组

为何在用户注销时使用 location.href 而非 Vue Router 的 router.push

在开发 Web 应用时&#xff0c;用户注销功能的设计看似简单&#xff0c;但背后隐藏着对状态管理、安全性和用户体验的深层考量。以下将详细探讨为何许多项目在注销跳转时选择 location.href&#xff08;强制刷新页面&#xff09;而非 Vue Router 的 router.push&#xff08;单页…

知识篇 | 低代码开发(Low-Code Development)是个什么东东?

一、低代码的起源与历史背景 低代码开发的核心理念可以追溯到上世纪80年代的第四代编程语言&#xff08;4GL&#xff09;和快速应用开发工具&#xff08;RAD&#xff09;&#xff0c;例如PowerBuilder和Visual Basic。这些工具通过图形化界面简化了开发流程&#xff0c;但受限于…