当面对很多数据要处理,并且需要导入导出Excel表格时。在使用POI时发现非常耗内存,这时GitHub上的阿里开源项目Easy Excel可以解决此类问题,它是基于java的读写Excel,十分省内存。本篇博文主要是总结其简单使用以及关于添加分类功能的实现。
创建项目导入依赖(此依赖依靠PIO模块,项目还需要导入PIO模块)
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.4</version>
</dependency>
创建测试的实体类DemoData
@Data
public class DemoData {
//设置excel表头名称
@ExcelProperty(value = "学生编号",index = 0)
private Integer sno;
@ExcelProperty(value = "学生姓名",index = 1)
private String sname;
}
其中index
对应excel表格中的列,value
是列的名称
写操作
public static void main(String[] args) {
//实现excel写的操作
//1 设置写入文件夹地址和excel文件名称
String filename = "D:\\write.xlsx";
//2 调用easyexcel里面的方法实现写操作
//write方法两个参数:第一个参数文件路径名称,第二个参数实体类class
EasyExcel.write(filename,DemoData.class).sheet("学生列表").doWrite(getData());
}
//创建方法返回list集合
private static List<DemoData> getData() {
List<DemoData> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setSno(i);
data.setSname("lucy"+i);
list.add(data);
}
return list;
}
读操作
public static void main(String[] args) {
//实现excel读操作
String filename = "D:\\write.xlsx";
EasyExcel.read(filename,DemoData.class,new ExcelListener()).sheet().doRead();
}
这里我们需要创建一个读取excel监听器:
//创建读取excel监听器
public class ExcelListener extends AnalysisEventListener<DemoData> {
//一行一行读取excel内容
@Override
public void invoke(DemoData demoData, AnalysisContext analysisContext) {
System.out.println("****"+demoData);
}
//读取表头内容
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext analysisContext) {
System.out.println("表头:"+headMap);
}
//读取完之后
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
以上是EsayExcel的一些基本使用,具体可以去看官方的文档。
表结构
DROP TABLE IF EXISTS `edu_subject`;
CREATE TABLE `edu_subject` (
`id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '课程类别ID',
`title` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '类别名称',
`parent_id` char(19) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL DEFAULT '0' COMMENT '父ID',
`sort` int(0) UNSIGNED NOT NULL DEFAULT 0 COMMENT '排序字段',
`gmt_create` datetime(0) NOT NULL COMMENT '创建时间',
`gmt_modified` datetime(0) NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_parent_id`(`parent_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '课程科目' ROW_FORMAT = Compact;
实体类EduSubject
和分类实体类SubjectData
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="EduSubject对象", description="课程科目")
public class EduSubject implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "课程类别ID")
@TableId(value = "id", type = IdType.ID_WORKER_STR)
private String id;
@ApiModelProperty(value = "类别名称")
private String title;
@ApiModelProperty(value = "父ID")
private String parentId;
@ApiModelProperty(value = "排序字段")
private Integer sort;
@ApiModelProperty(value = "创建时间")
private Date gmtCreate;
@ApiModelProperty(value = "更新时间")
private Date gmtModified;
}
@Data
public class SubjectData {
@ExcelProperty(index = 0)
private String oneSubjectName; //一级分类
@ExcelProperty(index = 1)
private String twoSubjectName; //二级分类
}
监听类SubjectExcelListener
public class SubjectExcelListener extends AnalysisEventListener<SubjectData> {
//因为SubjectExcelListener不能交给spring进行管理,需要自己new,不能注入其他对象
//不能实现数据库操作
//我们这里通过一个有参构造进行传值
public EduSubjectService subjectService;
public SubjectExcelListener() {
}
public SubjectExcelListener(EduSubjectService subjectService) {
this.subjectService = subjectService;
}
//读取excel内容,一行一行进行读取
@Override
public void invoke(SubjectData subjectData, AnalysisContext analysisContext) {
if(subjectData == null){
throw new GuliException(20001,"文件数据为空");
}
//一行一行读取,每次读取有两个值,第一个值一级分类,第二个值二级分类
//判断一级分类是否重复
EduSubject eduOneSubject = this.existOneSubject(subjectService,subjectData.getOneSubjectName());
if(eduOneSubject == null){
//没有相同的一级分类,进行添加
eduOneSubject = new EduSubject();
eduOneSubject.setParentId("0");
eduOneSubject.setTitle(subjectData.getOneSubjectName());
subjectService.save(eduOneSubject);
}
//获取一级分类id值
String pid = eduOneSubject.getId();
//添加二级分类
//判断二级分类是否重复
EduSubject eduTwoSubject = this.existTwoSubject(subjectService, subjectData.getTwoSubjectName(), pid);
if(eduTwoSubject == null) {
eduTwoSubject = new EduSubject();
eduTwoSubject.setParentId(pid);
eduTwoSubject.setTitle(subjectData.getTwoSubjectName());//二级分类名称
subjectService.save(eduTwoSubject);
}
}
//判断一级分类不能重复添加
private EduSubject existOneSubject(EduSubjectService subjectService, String name) {
QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
wrapper.eq("title",name);
wrapper.eq("parent_id","0");
EduSubject oneSubject = subjectService.getOne(wrapper);
return oneSubject;
}
//判断二级分类不能重复添加
private EduSubject existTwoSubject(EduSubjectService subjectService,String name,String pid) {
QueryWrapper<EduSubject> wrapper = new QueryWrapper<>();
wrapper.eq("title",name);
wrapper.eq("parent_id",pid);
EduSubject twoSubject = subjectService.getOne(wrapper);
return twoSubject;
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
实现类EduSubjectServiceImpl
@Service
public class EduSubjectServiceImpl extends ServiceImpl<EduSubjectMapper, EduSubject> implements EduSubjectService {
//添加课程分类
@Override
public void saveSubject(MultipartFile file,EduSubjectService subjectService) {
try {
//文件输入流
InputStream in = file.getInputStream();
EasyExcel.read(in, SubjectData.class,new SubjectExcelListener(subjectService)).sheet().doRead();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Controller层EduSubjectController
@RestController
@RequestMapping("/eduservice/subject")
@CrossOrigin
public class EduSubjectController {
@Autowired
private EduSubjectService subjectService;
//添加课程分类
//获取上传过来文件,把文件内容读取出来
@PostMapping("addSubject")
public R addSubject(MultipartFile file) {
//上传过来excel文件
subjectService.saveSubject(file,subjectService);
return R.ok();
}
}
这里用的是elementUI的一个后台管理模板,项目地址:element,把项目下载下来用npm 安装运行。
添加路由在index.js
中
{
path: '/subject',
component: Layout,
redirect: '/subject/list',
name: '课程分类管理',
meta: {
title: '课程分类管理', icon: 'example' },
children: [
{
path: 'list',
name: '课程分类列表',
component: () => import('@/views/edu/subject/list'),
meta: {
title: '课程分类列表', icon: 'table' }
},
{
path: 'save',
name: '添加课程分类',
component: () => import('@/views/edu/subject/save'),
meta: {
title: '添加课程分类', icon: 'tree' }
}
]
}
创建路由对应的页面save.vue
<template>
<div class="app-container">
<el-form label-width="120px">
<el-form-item label="Excel模板描述">
<el-tag type="info">excel模版说明</el-tag>
<el-tag>
<i class="el-icon-download"/>
<a :href="'/static/01.xlsx'">点击下载模版</a>
</el-tag>
</el-form-item>
<el-form-item label="选择Excel文件">
<el-upload
ref="upload"
:auto-upload="false"
:on-success="fileUploadSuccess"
:on-error="fileUploadError"
:disabled="importBtnDisabled"
:limit="1"
:action="BASE_API+'/eduservice/subject/addSubject'"
name="file"
accept="application/vnd.ms-excel">
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
<el-button
:loading="loading"
style="margin-left: 10px;"
size="small"
type="success"
@click="submitUpload">上传</el-button>
</el-upload>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
BASE_API: process.env.BASE_API, // 接口API地址
importBtnDisabled: false, // 按钮是否禁用,
loading: false
}
},
created() {
},
methods:{
//点击按钮上传文件到接口里面
submitUpload() {
this.importBtnDisabled = true
this.loading = true
// js: document.getElementById("upload").submit()
this.$refs.upload.submit()
},
//上传成功
fileUploadSuccess(response) {
//提示信息
this.loading = false
this.$message({
type: 'success',
message: '添加课程分类成功'
})
//跳转课程分类列表
//路由跳转
this.$router.push({
path:'/subject/list'})
},
//上传失败
fileUploadError() {
this.loading = false
this.$message({
type: 'error',
message: '添加课程分类失败'
})
}
}
}
</script>
EsayExcel在各方面性能还是很不错的,后期可以根据官方的API进行深入学习。