使用EasyExcel导出百万条数据

发布时间:2024年01月05日

使用EasyExcel导出百万条数据

应用是基于100W条数据进行的测试
首先:导入相关需要的依赖:

		<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.16</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.16</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.10</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.apache.poi</groupId>
                    <artifactId>poi-ooxml-schemas</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.20</version>
        </dependency>

        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.25</version>
            <scope>compile</scope>
        </dependency>
         

创建所需要的实体类,如下:

@Accessors(chain = true)
@Data
public class ExcelBean {

    @ExcelProperty("主键id")
    private String id;

    @ExcelProperty("姓名")
    private String name;

    @ExcelProperty("地址")
    private String address;

    @ExcelProperty("年龄")
    private Integer age;

    @ExcelProperty("数量")
    private Integer number;

    @NumberFormat("#.##")
    @ExcelProperty("身高")
    private Double high;

    @ExcelProperty("距离")
    private Double distance;

    @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
    @ExcelProperty("开始时间")
    private Date startTime;

    @ExcelProperty("结束时间")
    private Date endTime;
}

创建所用到的测试类,如下:

@Slf4j
public class writeExcelByApi {

    public static final String FILE_NAME = "C:\\Users\\861123001\\Desktop\\mqtt压测软件\\自造数据\\test_04.xlsx";
    // 每个 sheet 写入的数据
    public static final int NUM_PER_SHEET = 300000;
    // 每次向 sheet 中写入的数据(分页写入)
    public static final int NUM_BY_TIMES = 50000;

    @Test
    public void writeExcelByApi(){
        String fileName = FILE_NAME;
        log.info("导出excel名称={}",fileName);
        long startTime = System.currentTimeMillis();
        //调用api
        List<ExcelBean> date = getDate();
        EasyExcel.write(fileName,ExcelBean.class).sheet().doWrite(date);
        log.info("导出excel结束,数据量={},耗时={}ms", date.size(), System.currentTimeMillis() - startTime);
    }

    /**
     * 获取excel 导出的数据
     *
     * @return list 集合
     */
    public static List<ExcelBean> getDate(){
        log.info("开始生成数据");
        //创建返回数据集合
        List<ExcelBean> list = new ArrayList<>();
        Date date = new Date();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 2000000; i++) {
            //创建数据对象
            ExcelBean excelBean = new ExcelBean();
            ExcelBean excel = excelBean.setId(UUID.randomUUID().toString())
                    .setName("小明" + (10000 + i))
                    .setAddress("浙江省杭州市西湖")
                    .setAge(i)
                    .setNumber(i + 10000)
                    .setHigh(1.234 * i)
                    .setDistance(3.14 * i)
                    .setStartTime(date)
                    .setEndTime(date);

            list.add(excel);
        }
        log.info("数据生成结束,数据量={},耗时={}ms", list.size(), System.currentTimeMillis() - startTime);
        return list;
    }
}

EasyExcel 导出 excel 应用优化一:可以通过分sheet来解决超出100万的数据

	@Test
    public void writeExcelByMulSheet() {
        String fileName = FILE_NAME;
        log.info("导出excel名称={}",fileName);
        long startTime = System.currentTimeMillis();

        //获取数据
        List<ExcelBean> date = getDate();

        //获取sheet的个数
        int sheetNum = date.size() % NUM_PER_SHEET == 0 ? date.size() / NUM_PER_SHEET : date.size() / NUM_PER_SHEET + 1;

        //指定写入的文件
        ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelBean.class).build();
        for (int i = 0; i < sheetNum; i++) {
            long l = System.currentTimeMillis();

            //设置sheet的名字,每个sheet名称不能相同
            String sheetName = "sheet" + i;
            WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetName).build();

            //开始根结束行数
            int startNum = i * NUM_PER_SHEET;
            int endNum = i == sheetNum - 1 ? date.size() : (i + 1) *  NUM_PER_SHEET;

            excelWriter.write(date.subList(startNum, endNum), writeSheet);
            log.info("写入sheet={},数据量{}-{}={},耗时={}ms", sheetName, endNum, startNum, endNum - startNum, System.currentTimeMillis() - l);
        }
        //最好放在finally中
        excelWriter.finish();
        log.info("导出excel结束,总数据量={},耗时={}ms", date.size(), System.currentTimeMillis() - startTime);
    }

EasyExcel 导出 excel 应用优化二:数据源 list 太大,直接读取全部的 list 数据导致 OOM
将 list 数据进行分页读取,并进行分页写入到 excel。这样还有个好处,每次每页读取部分数据,然后写入到 excel 中(相当于该批数据已经从内存刷到了磁盘),也增加了写入的效率;poi 中的导出excel,为此专门提供了一个刷新磁盘的 api,具体代码如下

	@Test
    public void writeExcelByMulWrite() {
        String fileName = FILE_NAME;
        log.info("导出excel名称={}",fileName);
        long startTime = System.currentTimeMillis();

        //获取数据
        List<ExcelBean> date = getDate();
        ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelBean.class).build();

        //适用于针对100万数据以下的写法
        //WriteSheet writeSheet = EasyExcel.writerSheet("testSheet" ).build();

        //计算需要写的次数
        int times = date.size() % NUM_BY_TIMES == 0 ? date.size() / NUM_BY_TIMES : date.size() / NUM_BY_TIMES + 1;
        for (int i = 0; i < times; i++) {
            long l = System.currentTimeMillis();
            WriteSheet writeSheet = EasyExcel.writerSheet("testSheet" + i).build();
            //开始跟结束行数
            int startNum = i * NUM_BY_TIMES;
            int endNum = i == times - 1 ? date.size() : (i + 1) * NUM_BY_TIMES;

            excelWriter.write(date.subList(startNum, endNum), writeSheet);
            log.info("写入数量{}-{}={},耗时={}ms", endNum, startNum, endNum - startNum, startTime - l);
        }
        //最好写在finally里
        if (excelWriter != null) {
            excelWriter.finish();
        }
    }

EasyExcel 导出 excel 应用优化三:结合前面两种方案
将 list 数据进行分页读取,并且每个 sheet 分多次写入,且写入到多个 sheet 中

	@Test
    public void writeExcelByMulSheetAndMulWrite() {
        String fileName = FILE_NAME;
        log.info("导出excel名称={}", fileName);
        long startTime = System.currentTimeMillis();

        //获取数据
        List<ExcelBean> date = getDate();

        //获取sheet表数
        int sheetNum = date.size() % NUM_PER_SHEET == 0 ? date.size() / NUM_PER_SHEET : date.size() / NUM_PER_SHEET + 1;

        //获取每个sheet导入的次数
        int writeNumPerSheet = NUM_PER_SHEET % NUM_BY_TIMES == 0 ? NUM_PER_SHEET / NUM_BY_TIMES : NUM_PER_SHEET / NUM_BY_TIMES + 1;

        // 最后一个 sheet 写入的数量
        int writeNumLastSheet = date.size() - (sheetNum - 1) * NUM_PER_SHEET;

        // 最后一个 sheet 写入的次数
        int writeNumPerLastSheet = writeNumLastSheet % NUM_BY_TIMES == 0 ? writeNumLastSheet / NUM_BY_TIMES : writeNumLastSheet / NUM_BY_TIMES + 1;

        // 指定写入的文件
        ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelBean.class).build();

        for (int i = 0; i < sheetNum; i++) {
            String sheetName = "sheet" + i;
            WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetName).build();
            int writeNum = i == sheetNum - 1 ? writeNumPerLastSheet : writeNumPerSheet; // 每个sheet 写入的次数
            int endEndNum = i == sheetNum - 1 ? date.size() : (i + 1) * NUM_PER_SHEET; // 每个sheet 最后一次写入的最后行数

            for (int j = 0; j < writeNum; j++) {
                long l = System.currentTimeMillis();
                int startNum = i * NUM_PER_SHEET + j * NUM_BY_TIMES;
                int endNum = j == writeNum - 1 ? endEndNum : i * NUM_PER_SHEET + (j + 1) * NUM_BY_TIMES;
                excelWriter.write(date.subList(startNum, endNum), writeSheet);
                log.info("写入sheet={},数据量={}-{}={},耗时={}", sheetName, endNum, startNum, endNum - startNum, startTime - l);
            }
        }
        // 需要放入 finally 中
        if (excelWriter != null) {
            excelWriter.finish();
        }
        log.info("导出excel结束,总数据量={},耗时={}ms", date.size(), System.currentTimeMillis() - startTime);

    }

在这里插入图片描述

文章来源:https://blog.csdn.net/aaaaaaaaattt/article/details/135407493
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。