easyExcel使用案例有代码

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

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

EasyExcel 的主要特点如下:

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

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

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

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

入门使用

使用maven导入easyExcel坐标

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

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

@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、 先创建这个对象

@Getter
public class ExcelListener<T> extends AnalysisEventListener<T> {
    /**
     * 每读取一行就执行一次
     * @param t    返回的数据
     * @param analysisContext
     */
    //读取后对象存储在data集合中
    List<T> data=new ArrayList<>();
    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
          data.add(t);
    }

    /**
     *  都读取解析完毕后执行,   一般用于对资源的处理, 或对操作的补充
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        System.out.println("表格的解析完成了");
    }

2、 进行读测试

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对象
    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

@RestController
@RequestMapping("/admin/product/category")
public class CategoryController {
    @Autowired
    private 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

@Service
@Transactional
public class CategoryServiceImpl implements CategoryService {
    @Autowired
    private CategoryMapper categoryMapper;

     /**
     * 用户上传excel
     *  1 定义对应的实体类
     *  2 定义监听器
     *  3  读取数据
     *  4  上传到数据库
     * @param file
     */
    @Override
    public 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
     */
    @Override
    public 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();
        }
    }
}
 
  • 创建对应的导出实体类的监听器
@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);

    @Override
    public 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
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        saveData();
        log.info("所有数据解析完成!");
    }
    /**
     * 存储到数据库
     */
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", CategoryList.size());
        log.info("存储数据库成功!");
        categoryMapper.insertExcelData(CategoryList);
    }
}

3、mapper

@Mapper
public interface CategoryMapper {
    //查询所有分类  导出
    List<Category> selectAllCategory();
    // 导入excel
    void insertExcelData(List<CategoryExcelVo> categoryList);
}
  • 注意在导入excel的时候, id字段也需要我们自己处理, 否则在网页的父子的层级关系就显示不出来了. 我们是通过id 和pid 来判断层级关系的
    <select id="selectAllCategory" resultType="com.chen.model.entity.product.Category">
        select <include refid="columns"/> from category
        where  is_deleted=0
        order 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-upload
            class="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>
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}=resp
  if(code===200){
    dialogImportVisible.value=false
    ElMessage.success("上传成功")
     //刷新页面, 查询一下就行
    //进入后查询一级标题
    const {data} = await getCategoryListByParentId(0)
    //赋值
    list.value = data
  }
}
</script>

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

NVIDIA(英伟达) GPU 芯片架构发展史

GPU 性能的关键参数 CUDA 核心数量&#xff08;个&#xff09;&#xff1a;决定了 GPU 并行处理能力&#xff0c;在 AI 等并行计算类业务下&#xff0c;CUDA 核心越多性能越好。 显存容量&#xff08;GB&#xff09;&#xff1a;决定了 GPU 加载数据量的大小&#xff0c;在 AI…

FFMPEG利用H264+AAC合成TS文件

本次的DEMO是利用FFMPEG框架把H264文件和AAC文件合并成一个TS文件。这个DEMO很重要&#xff0c;因为在后面的推流项目中用到了这方面的技术。所以&#xff0c;大家最好把这个项目好好了解。 下面这个是流程图 从这个图我们能看出来&#xff0c;在main函数中我们主要做了这几步&…

获取Kernel32基地址

暴力搜索 32位在4G内存搜索有一定可行性&#xff0c;但是处理起来其实还是比较麻烦的&#xff0c;因为内存不可读会触发异常&#xff0c;需要对这些异常问题进行处理。 优化思路:缩小范围、增大搜索步长 (1)不优化&#xff0c;原始内存特征匹配&#xff0c;容易出错&#xf…

【 <一> 炼丹初探:JavaWeb 的起源与基础】之 Servlet 与 JSP 的协作:MVC 模式的雏形

<前文回顾> 点击此处查看 合集 https://blog.csdn.net/foyodesigner/category_12907601.html?fromshareblogcolumn&sharetypeblogcolumn&sharerId12907601&sharereferPC&sharesourceFoyoDesigner&sharefromfrom_link <今日更新> 一、Servl…

如何在Github上面上传本地文件夹

前言 直接在GitHub网址上面上传文件夹是不行的&#xff0c;需要一层一层创建然后上传&#xff0c;而且文件的大小也有限制&#xff0c;使用Git进行上传更加方便和实用 1.下载和安装Git Git - Downloads 傻瓜式安装即可 2.获取密钥对 打开自己的Github&#xff0c;创建SSH密钥&…

软件高级架构师 - 软件工程

补充中 测试 测试类型 静态测试 动态测试 测试阶段 单元测试中&#xff0c;包含性能测试&#xff0c;如下&#xff1a; 集成测试中&#xff0c;包含以下&#xff1a; 维护 遗留系统处置 高水平低价值&#xff1a;采取集成 对于这类系统&#xff0c;采取 集成 的方式&…

初始提示词(Prompting)

理解LLM架构 在自然语言处理领域&#xff0c;LLM&#xff08;Large Memory Language Model&#xff0c;大型记忆语言模型&#xff09;架构代表了最前沿的技术。它结合了存储和检索外部知识的能力以及大规模语言模型的强大实力。 LLM架构由外部记忆模块、注意力机制和语…

react中如何使用使用react-redux进行数据管理

以上就是react-redux的使用过程&#xff0c;下面我们开始优化部分&#xff1a;当一个组件只有一个render生命周期&#xff0c;那么我们可以改写成一个无状态组件&#xff08;UI组件到无状态组件&#xff0c;性能提升更好&#xff09;

Vue 监听器的魔法之旅:@Watch(‘form.productId’) vs @Watch(‘value’) 大揭秘!✨

以下是一篇技术博客&#xff0c;主题围绕 Watch(form.productId) 和 Watch(value) 这两个 watcher 的功能、区别及使用场景&#xff0c;基于 compare-form.vue 的代码。准备好一起探索 Vue 监听器的魔法了吗&#xff1f;&#x1f604; &#x1f604; Vue 监听器的魔法之旅&…

SqlSugar 语法糖推荐方式

//方式1&#xff1a;var dd _repository._Db.Queryable<ConfigAggregateRoot, UserRoleEntity>((o, p) > o.Id p.Id).Select((o, p) > new{o.Id,o.Remark,p.RoleId,});//方式2&#xff1a;不推荐使用&#xff0c;建议优先使用 Lambda 表达式&#xff0c;因为它更…

数据结构:八大排序(冒泡,堆,插入,选择,希尔,快排,归并,计数)详解

目录 一.冒泡排序 二.堆排序 三.插入排序 四.选择排序 五.希尔排序 六.快速排序 1.Lomuto版本&#xff08;前后指针法&#xff09; 2.Lomuto版本的非递归算法 3.hoare版本&#xff08;左右指针法&#xff09; 4.挖坑法找分界值&#xff1a; 七.归并排序 八.计数排序…

?算法1-4 小A点菜

题目描述 不过 uim 由于买了一些书&#xff0c;口袋里只剩 M 元 (M≤10000)。 餐馆虽低端&#xff0c;但是菜品种类不少&#xff0c;有 N 种 (N≤100)&#xff0c;第 i 种卖 ai​ 元 (ai​≤1000)。由于是很低端的餐馆&#xff0c;所以每种菜只有一份。 小 A 奉行“不把钱吃…

Linux设备驱动开发之摄像头驱动移植(OV5640)

驱动移植 这里用的是NXP提供的原厂linux内核源码&#xff0c;目的是学习ov5640相关摄像头驱动的移植。如图&#xff0c;下面是linux源码自带的ov5640的驱动相关代码&#xff1a; 这个是ov5640相关头文件&#xff1a; 新建一个文件夹保存这些ov5640的驱动文件&#xff0c;打算在…

DeepSeek使用手册分享-附PDF下载连接

本次主要分享DeepSeek从技术原理到使用技巧内容&#xff0c;这里展示一些基本内容&#xff0c;后面附上详细PDF下载链接。 DeepSeek基本介绍 DeepSeek公司和模型的基本简介&#xff0c;以及DeepSeek高性能低成本获得业界的高度认可的原因。 DeepSeek技术路线解析 DeepSeek V3…

ArcGIS Pro应用指南:如何为栅格图精确添加坐标信息

一、引言 在地理信息系统中&#xff0c;栅格图是一种重要的数据类型。 然而&#xff0c;有时我们从网络上获取的栅格图并不包含坐标信息&#xff0c;这使得它们难以与其他带有坐标信息的数据进行集成和分析。 为了解决这一问题&#xff0c;我们需要对栅格图进行地理配准&…

机器学习4-PCA降维

1 降维 在数据处理过程中&#xff0c;会碰到维度爆炸&#xff0c;维度灾难的情况&#xff0c;为了得到更精简更有价值的信息&#xff0c;我们需要进一步处理&#xff0c;用的方法就是降维。 降维有两种方式&#xff1a;特征抽取、特征选择 特征抽取&#xff1a;就是特征映射…

辛格迪客户案例 | 深圳善康医药科技GMP培训管理(TMS)项目

01 善康医药&#xff1a;创新药领域的探索者 深圳善康医药科技股份有限公司自2017年创立以来&#xff0c;便扎根于创新药研发领域&#xff0c;专注于成瘾治疗药物的研究、生产与销售。公司坐落于深圳&#xff0c;凭借自身独特的技术优势与研发实力&#xff0c;在行业内逐渐崭露…

前端基础之组件

组件&#xff1a;实现应用中局部功能代码和资源的集合 非单文件组件 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"…

vue 安装依赖npm install过程中报错npm ERR! cb() never called!

解决办法&#xff1a; 步骤 1&#xff1a;清理 npm 缓存 npm cache clean --force rm -rf node_modules package-lock.json 步骤 2&#xff1a;一个第三方 npm 工具包&#xff0c;功能是 自动重试失败的 npm install 操作&#xff0c;适用于网络不稳定或依赖源不可靠的场景 …

【Oracle学习笔记】1.数据库组成对象

在Oracle数据库中&#xff0c;数据库对象是用于存储、管理和操作数据的基本构建块。以下是Oracle数据库中常见的对象类型及其简要说明&#xff1a; 1. 表&#xff08;Table&#xff09; ● 定义&#xff1a;表是存储数据的基本结构&#xff0c;由行&#xff08;记录&#xff0…