EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。
EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
com.alibaba
easyexcel
2.1.1
这里的ExcelProperty表示在Excle表格中第一行,表头的列名称
@Data
public class Stu {
//设置表头名称
@ExcelProperty("学生编号")
private int sno;
//设置表头名称
@ExcelProperty("学生姓名")
private String sname;
}
package com.atguigu.excel;
import com.alibaba.excel.EasyExcel;
import java.util.ArrayList;
import java.util.List;
public class TestWrite {
public static void main(String[] args) {
//设置文件名称和路径
String fileName="D:\\桌面内容迁移\\测试创建excel.xlsx";
EasyExcel.write(fileName, User.class)
.sheet("写操作")
.doWrite(data());
}
//循环设置要添加的数据,最终封装到list集合中
private static List data() {
List list = new ArrayList();
for (int i = 0; i < 10; i++) {
User user = new User();
user.setId(i+1);
user.setName("张三");
list.add(user);
}
return list;
}
}
package com.atguigu.excel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.util.ConverterUtils;
import java.util.Map;
public class ExcelListener extends AnalysisEventListener {
//一行一行去读取excel内容并封装到User中
//默认从第二行读取数据,因为第一行一般都是表头
@Override
public void invoke(User user, AnalysisContext context) {
System.out.println(user.toString());
}
//读取表头内容
@Override
public void invokeHead(Map headMap, AnalysisContext context) {
//System.out.println("表头:"+headMap);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
}
}
package com.atguigu.excel;
import com.alibaba.excel.EasyExcel;
public class TestRead {
public static void main(String[] args) {
String fileName="D:\\桌面内容迁移\\测试创建excel.xlsx";
EasyExcel.read(fileName, User.class,new ExcelListener())
.sheet()
.doRead();
}
}
先写两个实体类,第一个是数据库对应的实体类(EasyExcel中要求实体类属性个数与表中表头列的名称个数一致,我们一般实体类还会自己添加一些其他属性,比如说多表联查还需要嵌入一个属性是另一个表的实体类,那么这就不符合EasyExcel的规范,所以要写一个实体类属性与表格完全一致的实体类),第二个是你业务中需要的实体类(可能添加了一些其他的属性值,一般我们开发中都是用的这个,不需要添加)
这里第一个是我重新写的实体类,第二个是原本的实体类,被我自己加了属性,大家可以进行对比
package com.atguigu.ggkt.vo.vod;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
/**
*
* Dict
*
*
* @author qy
*/
@Data
public class SubjectEeVo {
@ExcelProperty(value = "id" ,index = 0)
private Long id;
@ExcelProperty(value = "课程分类名称" ,index = 1)
private String title;
@ExcelProperty(value = "上级id" ,index = 2)
private Long parentId;
@ExcelProperty(value = "排序" ,index = 3)
private Integer sort;
}
package com.atguigu.ggkt.model.vod;
import com.atguigu.ggkt.model.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Data
@ApiModel(description = "Subject")
@TableName("subject")
public class Subject {
private static final long serialVersionUID = 1L;
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("create_time")
private Date createTime;
@ApiModelProperty(value = "更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField("update_time")
private Date updateTime;
@ApiModelProperty(value = "逻辑删除(1:已删除,0:未删除)")
@JsonIgnore
@TableLogic
@TableField("is_deleted")
private Integer isDeleted;
@ApiModelProperty(value = "其他参数")
@TableField(exist = false)
private Map param = new HashMap<>();
@ApiModelProperty(value = "类别名称")
@TableField("title")
private String title;
@ApiModelProperty(value = "父ID")
@TableField("parent_id")
private Long parentId;
@ApiModelProperty(value = "排序字段")
@TableField("sort")
private Integer sort;
@ApiModelProperty(value = "是否包含子节点")
@TableField(exist = false)
private boolean hasChildren;
}
package com.atguigu.ggkt.vod.service;
import com.atguigu.ggkt.model.vod.Subject;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
/**
*
* 课程科目 服务类
*
*
* @author atguigu
* @since 2023-04-19
*/
public interface SubjectService extends IService {
void exportData(HttpServletResponse response);
void importDictData(MultipartFile file);
}
package com.atguigu.ggkt.vod.service.impl;
import com.alibaba.excel.EasyExcel;
import com.atguigu.ggkt.model.vod.Subject;
import com.atguigu.ggkt.vo.vod.SubjectEeVo;
import com.atguigu.ggkt.vod.listener.SubjectListener;
import com.atguigu.ggkt.vod.mapper.SubjectMapper;
import com.atguigu.ggkt.vod.service.SubjectService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
/**
*
* 课程科目 服务实现类
*
*
* @author atguigu
* @since 2023-04-19
*/
@Service
public class SubjectServiceImpl extends ServiceImpl implements SubjectService {
@Resource
SubjectService subjectService;
@Resource
SubjectListener subjectListener;
//课程分类导出功能
@Override
public void exportData(HttpServletResponse response) {
try{
//设置从数据库下载值的信息
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和EasyExcel没有关系
String fileName = URLEncoder.encode("课程分类", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");
List subjects = subjectService.list();
List subjectEeVos = new ArrayList<>(subjects.size());
for(Subject dict : subjects) {
SubjectEeVo dictVo = new SubjectEeVo();
// dictVo.setId(dict.getId());
// dictVo.setParentId(dict.getParentId());
// 以上的赋值写法较为复杂,Java提供一个方法让一个对象的属性赋值给另一个对象
BeanUtils.copyProperties(dict,dictVo);
subjectEeVos.add(dictVo);
}
EasyExcel.write(response.getOutputStream(), SubjectEeVo.class)
.sheet("课程分类")
.doWrite(subjectEeVos);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void importDictData(MultipartFile file) {
try {
EasyExcel.read(file.getInputStream(),SubjectEeVo.class,subjectListener)
.sheet().doRead();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
没有使用swagger的记得把@ApiOperation注解删掉
package com.atguigu.ggkt.vod.controller;
import com.atguigu.ggkt.model.vod.Subject;
import com.atguigu.ggkt.result.Result;
import com.atguigu.ggkt.vod.service.SubjectService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
*
* 课程科目 前端控制器
*
*
* @author atguigu
* @since 2023-04-19
*/
@RestController
@RequestMapping("/admin/vod/subject")
@CrossOrigin(origins = "*")
public class SubjectController {
@Resource
SubjectService subjectService;
@ApiOperation("课程分类导出")
@GetMapping("/exportData")
public void exportData(HttpServletResponse response){
subjectService.exportData(response);
}
@ApiOperation(value = "导入")
@PostMapping("importData")
public Result importData(MultipartFile file) {
subjectService.importDictData(file);
return Result.ok(null);
}
}
exportData() {
window.open("http://localhost:8301/admin/vod/subject/exportData")
}
按钮
导入
点击对应的弹出层
点击上传
只能上传xls文件,且不超过500kb
data中添加弹出层的属性
data() {
return {
dialogImportVisible: false,
list:[] //数据字典列表数组
}
}
添加导入方法
importData() {
this.dialogImportVisible = true
},
onUploadSuccess(response, file) {
this.$message.info('上传成功')
this.dialogImportVisible = false
this.getSubList(0)
},
这里的this.getSubList(0)只是导入数据后调用重新加载页面数据的一个方法,可以自行编写
准备好我们要导入到数据库的表格
然后打开树形菜单,可以发现表格中的数据已经被渲染上去了
对service中的sersheet方法和doWrite、doRead方法有疑问的这里解答一下,sersheet是excel表格中底端的那个名称,doRead通过监听器每次读取一行,这也是EasyExcel的优点,一次一行,读完这行再下一行不会全部读取出来,举个极端例子。比如说你内存仅剩100kb了,你表格有500kb,那是不是一下子就蹦了?那读一行完加载到内存中,再下一行就可以很好的解决了这种问题,doWrite就是写入的操作了
以上就是 EasyExcel的读写操作并且与Element-ui进行整合实现导入导出功能