目录
EasyExcel特点
一.导入excel案例
二.读取excel的相关技术点
1.读取excel的方式
2.读取sheet数量
3.指定从第几行开始读数据
三.导出excel
1.前端发起请求
2.controller控制层
3.service层
4.实体类:复杂表头的情况,给@ExcelProperty的value赋值时,需要具备所有行的表头;使用注解@ExcelIgnore忽略导出的字段是否包含
5.设置导出excel的表头高宽:加在类上或者字段上
Java领域解析,生成Excel比较有名的框架有Apache poi,jxl等,但他们都存在一个严重的问题就是非常的耗内存,如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc.
EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称,EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。
EasyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)
如果在项目中已经使用了POI,再引入easyexcel可能会存在冲突,要注意对应的版本
org.apache.poi
poi
3.17
org.apache.poi
poi-ooxml
3.17
org.apache.poi
poi-scratchpad
3.17
com.alibaba
easyexcel
2.1.1
需求:校验导入excel表头是否正确,excel行转成实体对象出错时,获取错误信息
1.前端:使用bootstrap的fileinput控件,设置上传的链接及上传结束后的提示信息
$("#file").fileinput({
language : 'zh',
uploadUrl :basePath+"roadData/uploadExcelFileNew",
maxFileCount : 10,//表示允许同时上传的最大文件个数
showCaption : true,//是否显示标题
browseClass : "btn btn-primary", //按钮样式
showPreview:true,
allowedFileExtensions: ["xls", "xlsx"],
uploadAsync:true,
msgFilesTooLess:'您必须至少选择{n}个文件才能上传,请点击“浏览”选择您需要上传的文件!',
msgFilesTooMany:'超过了单次上传最大文件数{m}个,请分批次上传!',
previewFileIcon: '',
browseLabel : "浏览",
msgSelected:'添加了{n}个文件',
dropZoneTitle:'拖拽文件到这里 …
最多支持10个文件上传
',
uploadExtraData:function (previewId, index) {
var data = {
"isClearFlag": mini.get("isClearFlag").getValue()
};
return data;
},
previewFileIconSettings: {
'doc': '',
'xls': '',
'ppt': '',
'jpg': '',
'pdf': '',
'zip': '',
'htm': '',
'txt': '',
'mov': '',
'mp3': '',
},
layoutTemplates:{ //是否显示预览下的上传按钮
actionUpload:''
},
previewFileExtSettings: {
'doc': function(ext) {
return ext.match(/(doc|docx)$/i);
},
'xls': function(ext) {
return ext.match(/(xls|xlsx)$/i);
},
'ppt': function(ext) {
return ext.match(/(ppt|pptx)$/i);
},
'zip': function(ext) {
return ext.match(/(zip|rar|tar|gzip|gz|7z)$/i);
},
'htm': function(ext) {
return ext.match(/(php|js|css|htm|html)$/i);
},
'txt': function(ext) {
return ext.match(/(txt|ini|md)$/i);
},
'mov': function(ext) {
return ext.match(/(avi|mpg|mkv|mov|mp4|3gp|webm|wmv)$/i);
},
'mp3': function(ext) {
return ext.match(/(mp3|wav)$/i);
},
}
});
$("#file").on('fileuploaded', function(event, data, previewId, index) {
var message = mini.decode(data);
if(message.response.type == 200){
showMsg("文件上传成功","success",1800,"center","center",closeWindow());
}else{
showMsg(message.response.msg,"danger",3000,"center","center");
}
});
function closeWindow(action) {
if (window.CloseOwnerWindow) return window.CloseOwnerWindow(action);
else window.close();
}
2.controller控制层:文件上传调用的方法
/**
* @Description 上传文件--使用easyexcel方式解析
* @author qingyun
* @Date 2021年5月19日 上午10:26:03
*/
@RequestMapping("/uploadExcelFileNew")
public String uploadExcelFileNew(@RequestParam("file") MultipartFile file, HttpServletRequest request,HttpServletResponse response) {
return roadDataService.uploadExcelFileNew(file,request,response);
}
3.serice层:处理文件上传逻辑,这里定义一个工具类EasyExcelUtils进行处理,对处理结果根据Message标识判断是否解析正常,正常则设置其他字段值,把数据入库,不正常,则把message返回给前端
/**
* @Description 上传文件--使用easyexcel方式解析
* @author qingyun
* @Date 2021年5月19日 上午10:26:03
*/
public String uploadExcelFileNew(MultipartFile file, HttpServletRequest request, HttpServletResponse response) {
Message message = new Message();
EasyExcelUtils easyExcelUtils = new EasyExcelUtils(RoadData.class); //创建工具类时传递class,用于后面比对表头使用
try {
request.setCharacterEncoding("utf-8");
response.setCharacterEncoding("utf-8");
InputStream fileInput = file.getInputStream(); // 输入流
EasyExcel.read(fileInput,RoadData.class,easyExcelUtils).sheet().doRead();
message = easyExcelUtils.getMessage();
if(Message.OK == message.getType()) { //解析完成没有错误
List
4.EasyExcelUtils类:使用@Component注解修饰,使类加载进spring容器中进行管理;数据的类型使用Object进行定义,定义一个Mesage类用于记录解析和验证表头是否与模板一致的信息;创建EasyExcelUtils时,传递一个接收解析excel类的class对象,使用注解ExcelProperty获取到类的字段,并与excel中的进行比对,验证导入的excel是否符合要求。
/**
* @Description 使用easyexcel方式解析数据的工具类
* @author qingyun
* @Date 2021年5月19日 上午10:35:58
*/
@Component
public class EasyExcelUtils extends AnalysisEventListener
5.RoadData实体类:使用@ExcelProperty注解标识字段,指定index和value,用于接收excel的值,以及进行表头的比对
public class RoadData implements Serializable {
private static final long serialVersionUID = 1L;
private String id; //id
@ExcelProperty(value = "年份",index = 0)
private String repYear; //年份
@ExcelProperty(value = "养管单位",index = 1)
private String unit; //养管单位
@ExcelProperty(value = "路线",index = 2)
private String roadCode; //路线编码
@ExcelProperty(value = "上下行",index = 3)
private String roadDirectName; //行车方向
@ExcelProperty(value = "路段起点",index = 4)
private BigDecimal startStake; //起点桩号
@ExcelProperty(value = "路段终点",index = 5)
private BigDecimal endStake; //终点桩号
@ExcelProperty(value = "长度km",index = 6)
private BigDecimal roadLength; //长度
@ExcelProperty(value = "技术等级",index = 7)
private String roadGradeName; //技术等级
@ExcelProperty(value = "路面类型",index = 8)
private String paveTypeName; //路面类型
@ExcelProperty(value = "PQI",index = 9)
private BigDecimal pqi; //PQI
@ExcelProperty(value = "PCI",index = 10)
private BigDecimal pci; //PCI
@ExcelProperty(value = "RQI",index = 11)
private BigDecimal rqi; //rqi
@ExcelProperty(value = "RDI",index = 12)
private BigDecimal rdi; //rdi
@ExcelProperty(value = "DR",index = 13)
private BigDecimal dr; //dr
@ExcelProperty(value = "IRI",index = 14)
private BigDecimal iri; //iri
@ExcelProperty(value = "RD",index = 15)
private BigDecimal rd; //rd
private BigDecimal pci_score; //PCI得分
private BigDecimal rqi_score; //rqi得分
private BigDecimal rdi_score; //rdi得分
@ExcelProperty(value = "PQI分级",index = 16)
private String pqiGrade; //PQI分级
@ExcelProperty(value = "PCI分级",index = 17)
private String pciGrade; //PCI分级
@ExcelProperty(value = "RQI分级",index = 18)
private String rqiGrade; //RQI分级
@ExcelProperty(value = "RDI分级",index = 19)
private String rdiGrade; //RDI分级
@ExcelProperty(value = "区域",index = 20)
private String theAreaName; //区域
@ExcelProperty(value = "抽检性质",index = 21)
private String randomNature; //抽检性质
@ExcelProperty(value = "PCI整改后得分",index = 22)
private BigDecimal pciAfter; //PCI整改后得分
@ExcelProperty(value = "RQI整改后得分",index = 23)
private BigDecimal rqiAfter; //RQI整改后得分
@ExcelProperty(value = "RDI整改后得分",index = 24)
private BigDecimal rdiAfter; //RDI整改后得分
@ExcelProperty(value = "整改标识",index = 25)
private String rectificaFlag; //整改标识(未完成/已完成)
private String createUserCode;
private String createUserName;
private Date createTime;
}
(1)源码按路径path的方式
public static ExcelReaderBuilder read(String pathName, Class head, ReadListener readListener) {
ExcelReaderBuilder excelReaderBuilder = new ExcelReaderBuilder();
excelReaderBuilder.file(pathName);
if (head != null) {
excelReaderBuilder.head(head);
}
if (readListener != null) {
excelReaderBuilder.registerReadListener(readListener);
}
return excelReaderBuilder;
}
(2)按文件输入流的方式读
public static ExcelReaderBuilder read(InputStream inputStream, Class head, ReadListener readListener) {
ExcelReaderBuilder excelReaderBuilder = new ExcelReaderBuilder();
excelReaderBuilder.file(inputStream);
if (head != null) {
excelReaderBuilder.head(head);
}
if (readListener != null) {
excelReaderBuilder.registerReadListener(readListener);
}
return excelReaderBuilder;
}
(3)按file读取
public static ExcelReaderBuilder read(File file, Class head, ReadListener readListener) {
ExcelReaderBuilder excelReaderBuilder = new ExcelReaderBuilder();
excelReaderBuilder.file(file);
if (head != null) {
excelReaderBuilder.head(head);
}
if (readListener != null) {
excelReaderBuilder.registerReadListener(readListener);
}
return excelReaderBuilder;
}
(1)只能读取到最前面的一个sheet
EasyExcel.read(fileInput,RoadData.class,easyExcelUtils).sheet().doRead()
(2)读取所有的sheet:
EasyExcel.read(fileInput,RoadData.class,easyExcelUtils).doReadAll()
(3)读取指定的sheet:
ExcelReader excelReader = EasyExcel.read(fileInput).build();
// 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener
ReadSheet readSheet1 =
EasyExcel.readSheet(0).head(RoadData.class).registerReadListener(easyExcelUtils).build();
ReadSheet readSheet2 =
EasyExcel.readSheet(1).head(RoadData.class).registerReadListener(easyExcelUtils).build();
// 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
excelReader.read(readSheet1, readSheet2);
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
EasyExcel.read(fileInput,RoadData.class,easyExcelUtils).headRowNumber(1).doReadAll();
function download(){ //导出报表
var zqCode = "";
var year = "";
var params = {"zqCode":zqCode,"year":year};
var url = basePath + "roadData/download";
downloadFile(url, params); //组织和发送下载请求
}
//组织和发送下载请求
function downloadFile(path, params) {
$("#downloadform").remove();
var form = $("
@RequestMapping(value = { "/download" }, produces = { "text/html;charset=UTF-8" })
@ResponseBody
public void download(HttpServletRequest request, HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
//String id = request.getParameter("id");
roadDataService.download(response);
}
public void download(HttpServletResponse response) {
String fileName;
try {
fileName = "文件名称.xlsx";
fileName = new String(URLEncoder.encode(fileName, "UTF-8").getBytes(), "ISO-8859-1");
response.addHeader("Content-Disposition", " attachment;filename=" + fileName);
response.setContentType("application/octet-stream");
ServletOutputStream out = response.getOutputStream();
EasyExcel.write(out,RoadDataNew.class).sheet("学生列表").doWrite(getData());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* @Description 添加此类是处理复杂表头的情况
* @author qingyun
* @Date 2021年5月19日 下午4:51:26
*/
public class RoadDataNew implements Serializable {
private static final long serialVersionUID = 1L;
@ExcelIgnore
private String id; //id
@ExcelProperty(value = {"年份","年份"},index = 0)
private String repYear; //年份
@ExcelProperty(value = {"养管单位","养管单位"},index = 1)
private String unit; //养管单位
@ExcelProperty(value = {"路线情况","路线"},index = 2)
private String roadCode; //路线编码
@ExcelProperty(value = {"路线情况","上下行"},index = 3)
private String roadDirectName; //行车方向
@ExcelProperty(value = {"路段起点","路段起点"},index = 4)
private BigDecimal startStake; //起点桩号
@ExcelProperty(value = {"路段终点","路段终点"},index = 5)
private BigDecimal endStake; //终点桩号
@ExcelProperty(value = {"长度km","长度km"},index = 6)
private BigDecimal roadLength; //长度
@ExcelProperty(value = {"路面情况","技术等级"},index = 7)
private String roadGradeName; //技术等级
@ExcelProperty(value = {"路面情况","路面类型"},index = 8)
private String paveTypeName; //路面类型
@ExcelProperty(value = {"PQI","PQI"},index = 9)
private BigDecimal pqi; //PQI
@ExcelProperty(value = {"PCI","PCI"},index = 10)
private BigDecimal pci; //PCI
@ExcelProperty(value = {"RQI","RQI"},index = 11)
private BigDecimal rqi; //rqi
@ExcelProperty(value = {"RDI","RDI"},index = 12)
private BigDecimal rdi; //rdi
@ExcelProperty(value = {"DR","DR"},index = 13)
private BigDecimal dr; //dr
@ExcelProperty(value = {"IRI","IRI"},index = 14)
private BigDecimal iri; //iri
@ExcelProperty(value = {"RD","RD"},index = 15)
private BigDecimal rd; //rd
@ExcelIgnore
private BigDecimal pci_score; //PCI得分
@ExcelIgnore
private BigDecimal rqi_score; //rqi得分
@ExcelIgnore
private BigDecimal rdi_score; //rdi得分
@ExcelProperty(value = {"PQI分级","PQI分级"},index = 16)
private String pqiGrade; //PQI分级
@ExcelProperty(value = {"PCI分级","PCI分级"},index = 17)
private String pciGrade; //PCI分级
@ExcelProperty(value = {"RQI分级","RQI分级"},index = 18)
private String rqiGrade; //RQI分级
@ExcelProperty(value = {"RDI分级","RDI分级"},index = 19)
private String rdiGrade; //RDI分级
@ExcelProperty(value = {"区域","区域"},index = 20)
private String theAreaName; //区域
@ExcelProperty(value = {"抽检性质","抽检性质"},index = 21)
private String randomNature; //抽检性质
@ExcelProperty(value = {"PCI整改后得分","PCI整改后得分"},index = 22)
private BigDecimal pciAfter; //PCI整改后得分
@ExcelProperty(value = {"RQI整改后得分","RQI整改后得分"},index = 23)
private BigDecimal rqiAfter; //RQI整改后得分
@ExcelProperty(value = {"RDI整改后得分","RDI整改后得分"},index = 24)
private BigDecimal rdiAfter; //RDI整改后得分
@ExcelProperty(value = {"整改标识","整改标识"},index = 25)
private String rectificaFlag; //整改标识(未完成/已完成)
@ExcelIgnore
private String createUserCode;
@ExcelIgnore
private String createUserName;
@ExcelIgnore
private Date createTime;
}
导出截图
@HeadRowHeight(40) //设置投行高
@ContentRowHeight(20)//设置文本行高
@ColumnWidth(10) //设置列宽