文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

java实现excel的导出之使用easyExcel

2023-08-18 19:06

关注

前言

在我们的项目需求中,经常会遇到导出的需求,其中excel的导出最为常见。生成Excel比较有名的框架有Apache poi,jxl等,但他们都存在一个严重的问题就是非常的耗内存,如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc.

一、EasyExcel特点

EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称,
64M内存1分钟内读取75M(46W行25列)的Excel(当然还有急速模式能更快,但是内存占用会在100M多一点)。
EasyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。

不支持的功能

单个文件的并发写入、
2、读取读取图片
3、宏
4、csv读取(这个后续可能会考虑)

三、常见问题

读取文件务必使用2.0.5+(现在项目中用的是2.2.10)
2、读写反射对象用到了Cglib动态代理,所以成员变量必须符合驼峰规范,而且使用@Data不能使用@Accessors(chain = true)。后续会考虑支持非驼峰。
3、出现 NoSuchMethodException, ClassNotFoundException, NoClassDefFoundError。极大概率是jar冲突,建议clean项目,或者统一poi 的版本,理论上来说easyexcel兼容poi的3.17,4.0.1,4.1.0所有较新版本
4、用String去接收数字,出现小数点等情况这个是BUG,但是很难修复,后续版本会修复这个问题。目前请使用@NumberFormat注解,里面的参数就是调用了java自带的NumberFormat.format方法,不知道怎么入参的可以自己网上查询。
easyExcel的官方文档地址:https://alibaba-easyexcel.github.io/index.html

四、常用注解

4-1、读

ExcelProperty 指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。

@Getter@Setter@EqualsAndHashCode public class IndexOrNameData{//强制读取第三个这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去山配*@ExcelProperty(index = 2)private Double doubleDataprivate Stringstring;@ExcelProperty ("日期") private Date date;}

ExcelIgnore 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段

//强制读取第三个这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去山配*@ExcelIgnoreprivate Double doubleData

DateTimeFormat 日期转换,用String去接收excel日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat。

@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")private String date;

NumberFormat 数字转换,用String去接收excel数字格式的数据会调用这个注解。里面的value参照java.text.DecimalFormat。

@NumberFormat("#.##%") private String doubleData; //接收百比的数字

4-2、写

ExcelProperty index 指定写到第几列,默认根据成员变量排序。value指定写入的名称,默认成员变量的名字,多个value可以参照快速开始中的复杂头
ExcelIgnore 默认所有字段都会写入excel,这个注解会忽略这个字段
DateTimeFormat 日期转换,将Date写到excel会调用这个注解。里面的value参照java.text.SimpleDateFormat
NumberFormat 数字转换,用Number写excel会调用这个注解。里面的value参照java.text.DecimalFormat
ExcelIgnoreUnannotated 默认不加ExcelProperty 的注解的都会参与读写,加了不会参与

五、EasyExcel的使用

1、依赖

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

2、读excel

在这里插入图片描述

2.1最简单的

对象

@Datapublic class DemoData {    private String string;    private Date date;    private Double doubleData;}

controller类

@PostMapping("/outstoragcExce1")@Apioperation("读取出库excel表")public DeviceResponse storageservice(@RequestBody MultipartFile file) {try{storageservice.storageservice(file);} catch (Exception e){return new DeviceResponse(Constant.FAIL CODE,"出库导失败");}return new DeviceResponse(Constant.SUCCESS CODE,"出库导入成功");}

service实现类

@Autowiredprivate StorageService storageServicel@overridepublil void slorageservice(MulliparlFile file) {  Tnnutstream is = null:  try{     is=file.getInputstream();  } catch (IDException e){     e.printstackTrace();  }  //1.进行读取数数据,slorageReLrieval是我的puju类,  //2.new Soragelistenpr(storagpServire)这个是监听器,主要用来i取数据的,别急后面会讲  //3.特别注意的是storageservice这个service,我上面有注入进去 @Autowired,切记不要new会报错  EasyExcel.read(is,StorageRetrieval.class, new Soragelisterer(storageService))sheet().doRead();}

SorageListener监听器

@Componentpublic class SorageListener extends AnalysisEventListener<pojo类> {    private static final Logger LOGGER = LoggerFactory.getLogger(SorageListener.class);    //读取数据初始化值    private static final int BATCH_COUNT = 50;    List<pojo类> list = new ArrayList<pojo类>();    private StorageService storageService;    public SorageListener() {        storageService=new StorageServiceImpl();    }     public SorageListener(StorageService storageService) {        this.storageService = storageService;    }        @Override    public void invoke(StorageRetrieval data, AnalysisContext context) {        list.add(data);        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM        if (list.size() >= BATCH_COUNT) {            saveData();            // 存储完成清理 list            list.clear();        }    }    @Override    public void doAfterAllAnalysed(AnalysisContext context) {      if(list.size()==0){         return;      }        saveData();        LOGGER.info("所有数据解析完成!");    }        public void saveData() {        storageService.save(list); //代码实现类层保存数据        LOGGER.info("存储数据库成功!");    }}

2.2、指定列的下标或者列名

@Datapublic class IndexOrNameData {        @ExcelProperty(index = 2)    private Double doubleData;        @ExcelProperty("字符串")    private String string;    @ExcelProperty("日期")    private Date date;}@Testpublic void indexOrNameRead() {    String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";    // 这里默认读取第一个sheet    EasyExcel.read(fileName, IndexOrNameData.class, new IndexOrNameDataListener()).sheet().doRead();}

3、写excel

3.1最简单的

在这里插入图片描述
实体对象

@Data@piModel(value = "年龄统计实体类”public class FToWAgeStatisticalVo implements Serializable {private static final long serialVersionUID = -7891558029837989473L;@ApiModelProperty("区间")@ExcelProperty(value = "区间")private String ageGap;@ApiModeLProperty("病例数”)@ExcelProperty(value ="病例数)private Integer casesNumber ;@ApiModeProperty("密接数”)@ExceProperty(value = "密接数”)private Integer closeNumber ;}

service实现

@Overridepublic void avestatisticalExcel(Httpservlethesponse resonse,FlowReionStatisticalParam flowReionStatisticalParam) throws Exception{  //这里文件名如果涉及中文一定要使用URL编码,否则会乱码  String fileName = URLEncoder.encode( s: "floWAgeStatistical.xlsx" StandardCharsets.UTF_8.toString());  List<FloWAgeStatisticalVo> data = ageStatistical(flowRegionStatisticalParam);  response.setContentType("application/force-download");  response.setcharacterEncoding("utf-8");  response.setHeader( s: "Content-Disposition", s1: "attachment;filename=" + fileName);  EasyExcel.write(response.getoutputstream()FLoWAgeStatisticalVo.class)    .autoclosestream(true)    .exceType(ExcelTypeEnum.XLSX)    .sheet( sheetName: "年龄统计表")    .doWrite(data) ;}

3.2、列宽、行高

@Data@ContentRowHeight(10)@HeadRowHeight(20)@ColumnWidth(25)public class WidthAndHeightData {    @ExcelProperty("字符串")    private String string;    @ExcelProperty("日期")    private Date date;        @ColumnWidth(50)    @ExcelProperty("数字")    private Double doubleData;}

3.3、合并单元格

@Getter@Setter@EqualsAndHashCode// 将第6-7行的2-3列合并成一个单元格// @OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)public class DemoMergeData {    // 这一列 每隔2行 合并单元格    @ContentLoopMerge(eachRow = 2)    @ExcelProperty("字符串")    private String string;    @ExcelProperty("日期")    private Date date;    @ExcelProperty("数字")    private Double doubleData;}
 @Test public void mergeWrite() {     String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx";     // 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写     LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);     // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭     EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板")         .doWrite(data()); }

3.4、复杂头写入

在这里插入图片描述

@Data@ApiModel("学校学生缺勤信息")public class SchoolAnalyseVo {    @ApiModelProperty("学校Id")    @ExcelIgnore()    private Long schoolId;    @ExcelProperty("学校")    private String schoolName;    @ExcelProperty("学校类型")    private String schoolType;     ..........    @ExcelProperty({"症状", "发热"})    private String fever;    @ExcelProperty({"症状", "咳嗽"})    private String cough;    @ExcelProperty({"症状", "头痛"})    private String headache;    .........    @ExcelProperty({"疾病","普通感冒", "人数"})    private String commonColdNumber ;    @ExcelProperty({"疾病","普通感冒", "因病缺勤率"})    private String commonColdRate ;    @ExcelProperty({"疾病","流感", "人数"})    private String influenzaNumber;    .........}

3.5、日期、数字或者自定义格式转换

在这里插入图片描述

@Datapublic class ConverterData {        @ExcelProperty(value = "字符串", converter = CustomStringStringConverter.class)    private String string;        @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")    @ExcelProperty("日期")    private Date date;        @NumberFormat("#.##%")    @ExcelProperty(value = "数字")    private Double doubleData;}

自定义转换器

public class CustomStringStringConverter implements Converter<String> {    @Override    public Class supportJavaTypeKey() {        return String.class;    }    @Override    public CellDataTypeEnum supportExcelTypeKey() {        return CellDataTypeEnum.STRING;    }        @Override    public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty,        GlobalConfiguration globalConfiguration) {        return "自定义:" + cellData.getStringValue();    }        @Override    public CellData convertToExcelData(String value, ExcelContentProperty contentProperty,        GlobalConfiguration globalConfiguration) {        return new CellData(value);    }}

3.6、指定写入列

在这里插入图片描述

@Getter@Setter@EqualsAndHashCodepublic class IndexData {    @ExcelProperty(value = "字符串", index = 0)    private String string;    @ExcelProperty(value = "日期", index = 1)    private Date date;        @ExcelProperty(value = "数字", index = 3)    private Double doubleData;}

3.7、其他读操作

https://www.yuque.com/easyexcel/doc/write

4、填充excel

4.1 最简单的填充

在这里插入图片描述
对象

@Getter@Setter@EqualsAndHashCodepublic class FillData{private string name;private double number;private Date date;}

代码

@Testpublic void simpleFill() [    // 模板注 用]来表示你要用的变量 如果本来就有””,”]”特殊字符 用””]"代替    String templateFileName =TestFileUtil.getPath() + "demo" + File.separator + "fill" + File.separator + "simple.xlsx";    // 方案1 根据对象填充    String fileName = TestFileUtil,getPath() + "simpleFill" + System,currentTimeMillis() + ".xlsx",    // 这里 会填充到第一个sheet, 然后文件流会自动关闭    FillData fillData = new FillData();    fillData.setName("张一");    fillData.setNumber(5.2);    EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(fillData);    // 方案2 根据Map填充    fileName = TestFileUtil.getPath() + "simpleFill" + System.currentTimeMillis() + ".xlsx"    // 这里 会填充到第一个sheet, 然后文件流会自动关闭    Map<string, Object> map = new HashMap<string, Object>();    map.put("name”,"张二");    map .put("number", 5.2) :    EasyExcel.write(fileName).withTemplate(templateFileName).sheet().doFill(map);}

4.2、其他填充

填充列表、复杂的填充、数据量大的复杂填充、横向的填充、多列表组合填充填充
https://www.yuque.com/easyexcel/doc/fill

来源地址:https://blog.csdn.net/weixin_43945397/article/details/129532669

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯