zoukankan      html  css  js  c++  java
  • Alibaba EasyExcel初体验

    简介

    EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。相对于Apache POI来说,EasyExcel是从磁盘上一行行的读取数据,然后逐个解析,避免将大量数据加载到内存从而导致OOM。

    相关依赖:

    <dependency>
    	<groupId>com.alibaba</groupId>
    	<artifactId>easyexcel</artifactId>
    	<version>2.2.6</version>
    </dependency>
    
    <!-- 可选 -->
    <dependency>
    	<groupId>org.projectlombok</groupId>
    	<artifactId>lombok</artifactId>
    	<version>1.18.12</version>
    	<scope>provided</scope>
    </dependency>
    

    Excel导入

    需要导入的数据:

    Model类:

    
    @Data
    public class SchoolData {
    
        @ExcelProperty(value = "地址")
        private String address;
    
        @ExcelProperty(value = "学校名称")
        private String schoolName;
    
        @ExcelProperty(value = "邮政编码")
        private String postcode;
    
        @ExcelProperty(value = "收集时间")
        private Date collectDate;
    }
    

    使用@ExcelProperty注解时,可以使用两种方式将数据列和属性进行绑定:

    1. 通过value指定名称,将名称对应的excel列的数据绑定到属性。
    2. 通过index指定下标,将下标对应的excel列的数据绑定到属性。

    在不使用@ExcelProperty注解时,默认Model类属性和excel列数据按照先后顺序进行绑定的。

    监听器:

    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.event.AnalysisEventListener;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Map;
    
    public class SchoolDataListener extends AnalysisEventListener<SchoolData> {
    
        List<SchoolData> schoolDataList = new ArrayList<>();
    
        //需要传参时,通过构造方法传进来
        public SchoolDataListener() {
        }
    
        /**
         * 每解析一行数据都会进行调用
         */
        @Override
        public void invoke(SchoolData data, AnalysisContext context) {
            //一般数据量大的时候应该分批处理,防止数据全部加载到内存,导致OOM。
            schoolDataList.add(data);
        }
    
        /**
         * 数据全部解析完成时调用
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            System.out.println("数据读取完毕:");
            schoolDataList.stream().forEach(System.out::println);
        }
    
        /**
         * 每解析一行表头都会进行调用
         */
        @Override
        public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
            System.out.println("表头:");
            headMap.forEach((k, v) -> System.out.println(v));
            System.out.println();
        }
    }
    
    

    导入测试:

    导入有以下两种写法,一般使用简写的方式就行,当需要实现复杂的功能时可能会需要另外一种复杂的写法。

    @Test
    public void importExcel() {
    	ExcelReader excelReader = null;
    	try {
    		excelReader = EasyExcel.read("F:\测试数据.xlsx", SchoolData.class, new SchoolDataListener()).build();
    		ReadSheet readSheet = EasyExcel.readSheet(0).build();
    		excelReader.read(readSheet);
    	} finally {
    		if (excelReader != null) {
    			// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
    			excelReader.finish();
    		}
    	}
    }
    
    @Test
    public void importExcelSimple() {
    	//sheet() 默认读取第一个sheet
    	EasyExcel.read("F:\测试数据.xlsx", SchoolData.class, new SchoolDataListener()).sheet().doRead();
    }
    

    输出结果:

    表头:
    学校名称
    地址
    邮政编码
    收集时间
    
    数据读取完毕:
    SchoolData(address=晋宁县古城镇旧寨村  , schoolName=晋宁县古城镇古城中学, postcode=650604, collectDate=Sun Jan 05 00:00:00 CST 2020)
    SchoolData(address=云南省昆明市晋宁县二街乡老高村  , schoolName=晋宁县二街中学, postcode=650608, collectDate=Mon Jan 06 00:00:00 CST 2020)
    SchoolData(address=云南省昆明市晋宁县晋城镇寺林街  , schoolName=晋宁县晋城中学, postcode=650605, collectDate=Tue Jan 07 00:00:00 CST 2020)
    SchoolData(address=云南省晋宁县六街乡中学  , schoolName=晋宁县六街中学, postcode=650609, collectDate=Wed Jan 08 00:00:00 CST 2020)
    SchoolData(address=云南省昆明市晋宁县新街乡文河村  , schoolName=晋宁县新街中学, postcode=650606, collectDate=Thu Jan 09 00:00:00 CST 2020)
    SchoolData(address=晋宁县中和乡普照路  , schoolName=晋宁县中和中学, postcode=650600, collectDate=Fri Jan 10 00:00:00 CST 2020)
    SchoolData(address=云南省昆明市晋宁县上蒜乡上蒜中学  , schoolName=晋宁县上蒜乡上蒜中学, postcode=650607, collectDate=Sat Jan 11 00:00:00 CST 2020)
    SchoolData(address=云南省昆明市晋宁县化乐乡化乐村  , schoolName=晋宁县化乐乡中学, postcode=650611, collectDate=Sun Jan 12 00:00:00 CST 2020)
    SchoolData(address=晋宁县夕阳乡夕阳街夕阳民族中学  , schoolName=晋宁县夕阳民族中学, postcode=650603, collectDate=Mon Jan 13 00:00:00 CST 2020)
    SchoolData(address=云南省昆明市晋宁县宝峰镇古城村委会小古城村  , schoolName=晋宁县宝峰镇宝峰中学, postcode=650601, collectDate=Tue Jan 14 00:00:00 CST 2020)
    SchoolData(address=晋宁县双河乡椿树营  , schoolName=晋宁县双河民族中学, postcode=650602, collectDate=Wed Jan 15 00:00:00 CST 2020)
    SchoolData(address=昆明市富民县永定镇环城南路7号永定中学  , schoolName=富民县永定中学, postcode=650400, collectDate=Thu Jan 16 00:00:00 CST 2020)
    SchoolData(address=富民县永定镇大西山村  , schoolName=富民县勤劳中学, postcode=650400, collectDate=Fri Jan 17 00:00:00 CST 2020)
    SchoolData(address=富民县大营镇大营街16号  , schoolName=富民县大营中学, postcode=650400, collectDate=Sat Jan 18 00:00:00 CST 2020)
    SchoolData(address=云南省昆明市罗免乡者北村委会者北街  , schoolName=富民县第二中学, postcode=650401, collectDate=Sun Jan 19 00:00:00 CST 2020)
    

    Excel导出

    模拟生成数据:

    public class DataGenerate {
    
        /**
         * 数据生成
         */
        public static List<SchoolData> data() {
            List<SchoolData> schoolDataList = new ArrayList<>();
            SchoolData schoolData;
            for (int i = 0; i < 15; i++) {
                schoolData = new SchoolData();
                schoolData.setSchoolName("第" + i + "XXX中学");
                schoolData.setAddress("云南省昆明市XXXXX");
                schoolData.setPostcode("123456");
                schoolData.setCollectDate(new Date());
                schoolDataList.add(schoolData);
            }
    
            return schoolDataList;
        }
    }
    

    测试导出:

    同导入,导出也有以下两种写法。LongestMatchColumnWidthStyleStrategy用来设置自动列宽,如果需要设置列宽、行高等属性,可以在Model类上添加对应注解进行设置,如@ColumnWidth、@ContentRowHeight、@HeadRowHeight等等。

    @Test
    public void exportExcel() {
    	ExcelWriter excelWriter = null;
    	try {
    		excelWriter = EasyExcel.write("F:\测试数据_副本.xlsx", SchoolData.class).build();
    		WriteSheet writeSheet = EasyExcel.writerSheet("学校数据").registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
    		excelWriter.write(DataGenerate.data(), writeSheet);
    	} finally {
    		// 千万别忘记finish 会帮忙关闭流
    		if (excelWriter != null) {
    			excelWriter.finish();
    		}
    	}
    }
    
    @Test
    public void exportExcelSimple() {
    	EasyExcel.write("F:\测试数据_副本.xlsx", SchoolData.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("学校数据").doWrite(DataGenerate.data());
    }
    

    导出结果:

    基于Excel模板的填充

    模板:

    {} 代表普通变量 {.} 代表是list的变量。

    模板填充测试:

    @Test
    public void excelTemplateFill() {
    	ExcelWriter excelWriter = EasyExcel.write("F:\测试数据_副本3.xlsx").withTemplate("F:\模板.xlsx").build();
    	WriteSheet writeSheet = EasyExcel.writerSheet().build();
    	FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
    
    	//填充列表数据
    	excelWriter.fill(DataGenerate.data(), fillConfig, writeSheet);
    
    	//填充普通数据
    	Map<String, Object> map = new HashMap<String, Object>();
    	map.put("name", "狗子");
    	map.put("date", "2021-02-19");
    	excelWriter.fill(map, writeSheet);
    	excelWriter.finish();
    }
    

    填充结果:

    Web中的导入和导出

    import com.alibaba.excel.EasyExcel;
    import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.net.URLEncoder;
    
    @RestController
    public class SchoolDataController {
    
        /**
         * 导入Excel
         */
        @PostMapping("/import")
        public String importExcel(MultipartFile file) throws IOException {
            EasyExcel.read(file.getInputStream(), SchoolData.class, new SchoolDataListener()).sheet().doRead();
            return "success";
        }
    
        /**
         * 导出Excel
         */
        @GetMapping("/export")
        public void exportExcel(HttpServletResponse response) throws IOException {
            response.setContentType("application/vnd.ms-excel");
            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(), SchoolData.class).autoCloseStream(Boolean.FALSE).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("学校数据").doWrite(DataGenerate.data());
        }
    }
    

    参考:官方文档

  • 相关阅读:
    寒江独钓(0):内核开发上机指导
    异常:Hibernate数据库恢复错误
    天书夜读:从汇编语言到Windows内核编程笔记(4)
    企业WEBGIS网站解决方案
    如何使用国际开源项目构建一个完整的GIS(地理信息)应用系统
    将指定文件夹下的所有文件copy到目标文件夹下
    一些jquery的小知识
    压缩指定目录下指定文件(包括子目录下的文件)
    解压一个rar文件
    关于下载txt文本文挡的问题
  • 原文地址:https://www.cnblogs.com/seve/p/14432615.html
Copyright © 2011-2022 走看看