【EasyExcel详细步骤】(内附源码)
页面预览
数据导出
数据导入
第01章-Alibaba EasyExcel
1、EasyExcel介绍
1.1、EasyExcel的作用
- 数据导入:减轻录入工作量
- 数据导出:统计信息归档
- 数据传输:异构系统之间数据传输
1.2、EasyExcel的特点
快速:快速的读取excel中的数据。
简洁:映射excel和实体类,让代码变的更加简洁。
大文件:在读写大文件的时候使用磁盘做缓存,更加的节约内存。
2、快速开始
参考文档地址:https://easyexcel.opensource.alibaba.com/index.html
service-cmn模块添加依赖
<!--easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
</dependency>
2.1、最简单的写
在测试目录下创建以下文件
实体类:
package com.atguigu.syt.cmn.easyexcel;
@Getter
@Setter
@EqualsAndHashCode
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
测试用例:
package com.atguigu.syt.cmn.easyexcel;
public class ExcelWriteTest {
/**
* 最简单的写
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 直接写即可
*/
@Test
public void simpleWrite07() {
// 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入
// 写法2
String fileName = "d:/simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
@Test
public void simpleWrite03() {
String fileName = "d:/simpleWrite" + System.currentTimeMillis() + ".xls";
EasyExcel.write(fileName, DemoData.class).excelType(ExcelTypeEnum.XLS).sheet("模板").doWrite(data());
}
private List<DemoData> data() {
List<DemoData> list = ListUtils.newArrayList();
//算上标题,最多可写65536行(.xls)
//算上标题,最多可写1048576行(.xlsx)
//java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
}
2.2、最简单的读
在测试目录下创建以下文件
监听器:
package com.atguigu.syt.cmn.easyexcel;
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener<DemoData> {
/**
* 每隔3条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 3;
/**
* 缓存的数据
*/
private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 这个每一条数据解析都会来调用
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
log.info("解析到一条数据:{}", data);
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
// demoDAO.save(cachedDataList);
log.info("存储数据库成功!");
}
}
测试用例:
package com.atguigu.syt.cmn.easyexcel;
public class ExcelReadTest {
/**
* 最简单的读
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
* <p>
* 3. 直接读即可
*/
@Test
public void simpleRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法3:
String fileName = "d:/demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
}
第02章-数据导出
1、后端接口
参考文档:写Excel | Easy Excel (alibaba.com)
1.1、Controller
AdminRegionController
/**
* 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel)
*
* @since 2.1.1
*/
@ApiOperation(value="导出")
@GetMapping(value = "/exportData")
public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
try {
//int i = 9/0;
List<RegionExcelVo> regionExcelVoList = regionService.findRegionExcelVoList();
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("数据字典", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
// 这里需要设置不关闭流
EasyExcel.write(response.getOutputStream(), RegionExcelVo.class)
.sheet("数据字典")
.doWrite(regionExcelVoList);
} catch (Exception e) {
log.error(ExceptionUtils.getStackTrace(e));
// 重置response
response.reset();
response.setContentType("text/html");
response.setCharacterEncoding("utf-8");
response.getWriter().println("导出失败");
}
}
1.2、Service
接口:RegionService
/**
* 获取地区VO列表
* @return
*/
List<RegionExcelVo> findRegionExcelVoList();
实现:RegionServiceImpl
@Override
public List<RegionExcelVo> findRegionExcelVoList() {
List<Region> regionList = this.list();
List<RegionExcelVo> regionExcelVoList = ListUtils.newArrayListWithCapacity(regionList.size());
for(Region region : regionList) {
RegionExcelVo regionExcelVo = new RegionExcelVo();
BeanUtils.copyProperties(region, regionExcelVo);
regionExcelVoList.add(regionExcelVo);
}
return regionExcelVoList;
}
2、前端页面
在cmn/region/list.vue文件中添加如下代码
2.1、html
在表格上方添加如下代码
<div style="margin-bottom:5px;">
<el-button type="success" icon="el-icon-top" size="mini" @click="exportData"><i class="fa fa-plus" />导出</el-button>
</div>
2.2、脚本
在脚本中添加如下methods
引入模块:
import store from '@/store'
import axios from 'axios'
定义脚本:
exportData() {
//此种方式无法传递token
//window.open(process.env.VUE_APP_BASE_API + '/admin/cmn/region/exportData')
//使用当前方式传递token
const config = {
method: 'get',
url: '/dev-api/admin/cmn/region/exportData',
headers: {
'token': store.getters.token //
},
responseType: 'blob'
}
axios(config).then(response => {
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a')
link.href = url
link.setAttribute('download', '数据字典.xlsx')
document.body.appendChild(link)
link.click()
})
},
第03章-数据导入
1、后端接口
1.1、Controller
AdminRegionController
@ApiOperation(value = "导入")
@ApiImplicitParam(name = "file", value = "文件", required = true)
@PostMapping("/importData")
public Result importData(MultipartFile file) {
regionService.importData(file);
return Result.ok();
}
1.2、Service
接口:RegionService
/**
* 地区数据导入Excel
* @param file
*/
void importData(MultipartFile file);
实现:RegionServiceImpl
@Override
public void importData(MultipartFile file) {
try {
long b = System.currentTimeMillis();
EasyExcel.read(file.getInputStream(),RegionExcelVo.class, new RegionExcelListener(baseMapper)).sheet().doRead();
long e = System.currentTimeMillis();
log.info("用时:" + (e - b) + "ms" );
} catch (IOException e) {
throw new GuiguException(ResultCodeEnum.FAIL, e);
}
}
1.3、监听器
RegionExcelListener
package com.atguigu.syt.cmn.listener;
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class RegionExcelListener implements ReadListener<RegionExcelVo> {
/**
* 每隔3条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
/**
* 缓存的数据
*/
private List<RegionExcelVo> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private RegionMapper regionMapper;
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param regionMapper
*/
public RegionExcelListener(RegionMapper regionMapper) {
this.regionMapper = regionMapper;
}
/**
* 这个每一条数据解析都会来调用
*/
@Override
public void invoke(RegionExcelVo data, AnalysisContext context) {
log.info("解析到一条数据:{}", data);
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
regionMapper.saveBatch(cachedDataList);
log.info("存储数据库成功!");
}
}
1.4、Mapper
接口:RegionMapper
/**
* 根据RegionExcelVo列表批量保存数据
* @param cachedDataList
*/
void saveBatch(@Param("list") List<RegionExcelVo> cachedDataList);
实现:RegionMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.syt.cmn.mapper.RegionMapper">
<insert id="saveBatch">
INSERT INTO region ( id, code, parent_code, name, level )
VALUES
<foreach collection="list" item="region" separator=",">
(
#{region.id},
#{region.code},
#{region.parentCode},
#{region.name},
#{region.level}
)
</foreach>
</insert>
</mapper>
1.5、service的pom.xml配置
程序发布时,默认情况下java目录下的xml文件不会被发布,需要进行以下配置
<build>
<!-- 项目打包时会将java目录中的*.xml文件也进行打包 -->
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.yml</include>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
2、前端页面
在cmn/region/list.vue文件中添加如下代码
2.1、添加导入按钮
<el-button type="primary" icon="el-icon-bottom" size="mini" @click="importData"><i class="fa fa-plus" />导入</el-button>
2.2、添加导入弹出层
<el-dialog title="导入" :visible.sync="dialogImportVisible" width="480px">
<el-form label-position="right" label-width="100px">
<el-form-item label="请选择文件">
<!-- 注意:这里使用headers属性传递了token -->
<el-upload
v-loading="uploadLoading"
element-loading-text="数据导入中"
:headers="{ token }"
:multiple="false"
:on-progress="onUploadProgress"
:on-success="onUploadSuccess"
:on-error="onUploadError"
:action="VUE_APP_BASE_API + '/admin/cmn/region/importData'">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">
只能上传xls文件,且不超过1M
</div>
</el-upload>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button @click="dialogImportVisible = false">取消</el-button>
</div>
</el-dialog>
2.3、添加导入弹出层属性
data:
dialogImportVisible: false,
uploadLoading: false, //文件上传状态
VUE_APP_BASE_API: process.env.VUE_APP_BASE_API, //接口地址
token: store.getters.token,
2.4、添加导入方法
methods:
//显示一个文件选择对话框
importData() {
this.dialogImportVisible = true
},
//文件上传中
onUploadProgress(){
this.uploadLoading = true
},
onUploadSuccess(response) {
this.uploadLoading = false
//文件导入成功
if (response.code === 200) {
this.$message.success(response.message)
this.dialogImportVisible = false
this.getDictList(1)
} else {
//导入失败
this.$message.error(response.message)
this.dialogImportVisible = false
}
},
//导入失败
onUploadError() {
this.uploadLoading = false
this.$message.error('系统错误')
this.dialogImportVisible = false
},
源码:https://gitee.com/dengyaojava/guigu-syt-parent
【EasyExcel详细步骤】(内附源码)的更多相关文章
- SSM框架整合 详细步骤(备注) 附源码
整合思路 将工程的三层结构中的JavaBean分别使用Spring容器(通过XML方式)进行管理. 整合持久层mapper,包括数据源.会话工程及mapper代理对象的整合: 整合业务层Service ...
- 从零实现在线云相亲APP|程序员脱单神器(内附源码Demo)
实时音视频通话涉及到的技术栈.人力成本.硬件成本非常大,一般个人开发者基本无法独立完成一个功能健全并且稳定的实时音视频应用.本文介绍一天之内,无任何实时音视频低层技术的android开发者完成实时相亲 ...
- Spring中@Transactional事务回滚(含实例详细讲解,附源码)
一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除 ...
- Duboo整合SpringBoot超级详细例子(附源码)
dubbo3.0整合SpringBoot例子 dubbo新版本(3.0以上)在相对于 dubbo 旧版本(2.5.2.6.2.7),有很多的不相同的地方. 官方文档也说了新版本的特性: https:/ ...
- python爬虫爬取网易云音乐(超详细教程,附源码)
一. 前言 先说结论,目前无法下载无损音乐,也无法下载vip音乐. 此代码模拟web网页js加密的过程,向api接口发送参数并获取数据,仅供参考学习,如果需要下载网易云音乐,不如直接在客户端下载,客户 ...
- 基于电商直播SDK快速实现一个淘宝直播APP【内附源码】
现在各大互联网APP都标配电商直播带货了,没有直播带货开发经验都感觉自己跟不上技术的进步.今天快速基于Java实现一个安卓端电商直播APP,深入理解整个电商直播开发流程.我们最终实现效果如下: 按照惯 ...
- 干货:Java多线程详解(内附源码)
线程是程序执行的最小单元,多线程是指程序同一时间可以有多个执行单元运行(这个与你的CPU核心有关). 在java中开启一个新线程非常简单,创建一个Thread对象,然后调用它的start方法,一个 ...
- jquery省市区三级联动(数据来源国家统计局官网)内附源码下载
很久很久没有写博了. 今天更新了项目的省市区三级联动数据,更新后最新的海南三沙都有,分享给所有需要的小伙伴们... JQUERY + JSON,无数据库,纯JS代码,无加密,无压缩,可直接使用在任何项 ...
- java小项目之:抽奖系统!java初学者必备(内附源码)
[Java]Java摇奖源码,Java抽奖源码,Java随机抽奖源码 任务描述 本次任务要求为某商场开发一套幸运抽奖系统,客户必须首先注册成为该商场会员,会员登录成功后,就可以参加抽奖活动了.注册 用 ...
- 如何0代码实现多人音视频通话?【内附源码/Demo】
3月15日新增"1860+1194",全国进入了抗疫关键时期.响应政策多地采取了社会面清零策略. 3月14日零点,深圳按下了暂停键. 应疫情防控要求,深圳全市暂停生产经营活动,严格 ...
随机推荐
- vim 从嫌弃到依赖(18)——查找模式进阶
上一篇文章中,我们初步结识了如何使用查找模式,也能够通过n和 N进行查找.这篇将会介绍搜索中更高级的用法.另外在写上一篇文章的时候我发现介绍查找相关内容的时候不能用动图来演示,主要是因为输入的内容太多 ...
- TienChin 渠道管理-渠道类型
在上一篇文章当中,表里面有一个渠道类型,我们这节主要是将这个渠道类型创建好,首先我们来看看字典表. sys_dict_type 表: 字段名 数据类型 注释 dict_id bigint 字典主键 d ...
- 设计模式学习-使用go实现组合模式
组合模式 定义 适用范围 优点 缺点 代码实现 参考 组合模式 定义 组合模式(Composite),将对象组合成树形结构以表示'部分-整体'的层次关系.组合模式使得用户对单个对象和组合对象的使用具有 ...
- 强化学习基础篇[3]:DQN、Actor-Critic详细讲解
强化学习基础篇[3]:DQN.Actor-Critic详细讲解 1.DQN详解 1.1 DQN网络概述及其创新点 在之前的内容中,我们讲解了Q-learning和Sarsa算法.在这两个算法中,需要用 ...
- [转发]MySQL安装配置教程(超级详细、保姆级)
MySQL安装配置教程(超级详细.保姆级)_SoloVersion的博客-CSDN博客_mysql安装配置教程一. 下载MySQLMysql官网下载地址https://downloads.mysql. ...
- C++ Boost 异步网络编程基础
Boost库为C++提供了强大的支持,尤其在多线程和网络编程方面.其中,Boost.Asio库是一个基于前摄器设计模式的库,用于实现高并发和网络相关的开发.Boost.Asio核心类是io_servi ...
- 8.5 Windows驱动开发:内核注册表增删改查
注册表是Windows中的一个重要的数据库,用于存储系统和应用程序的设置信息,注册表是一个巨大的树形结构,无论在应用层还是内核层操作注册表都有独立的API函数可以使用,而在内核中读写注册表则需要使用内 ...
- AOKO奥科美2.5英寸外置硬盘盒开箱
上次在坛子里发布了一个帖子,然后根据坛友们的反馈,换购了另一个SATA固态硬盘.另一个是配套的硬盘盒,当时在某宝上搜了一圈,最终购买了这款硬盘盒,主要是因为它的外观,旁边有散热片.这款硬盘盒在某宝上不 ...
- Leetcode刷题第一天-贪心
455-分饼干 链接:455. 分发饼干 - 力扣(LeetCode) 优先使用最小饼干满足最小胃口,一个娃只能分一个饼干T_T不能加 1 class Solution: 2 def findCont ...
- Linux进程通信-POSIX IPC
前言 Linux POSIX IPC的可移植性是不如System V IPC的,但是我们只用Linux,并且内核版本高于2.6.6的话就不存在该问题了.也因为POSIX IPC出现的比较晚,借鉴了sy ...