Hutool-Excel大数据生成-XXOO

一、准备

对于大量数据输出,采用ExcelWriter容易引起内存溢出,因此有了BigExcelWriter。

* ExcelUtil Excel工具类,读取的快捷方法都被封装于此

* ExcelReader Excel读取器,Excel读取的封装,可以直接构造后使用。

* ExcelWriter Excel生成并写出器,Excel写出的封装(写出到流或者文件),可以直接构造后使用。

<!--hutool common 工具包-->
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
			<version>5.0.7</version>
		</dependency>

        <!--说明 hutool-4.x的poi-ooxml 版本需高于 3.17(别问我3.8版本为啥不行,因为3.17 > 3.8 )
        hutool-5.x的poi-ooxml 版本需高于 4.1.2 xercesImpl版本高于2.12.0-->
        <!--poi-ooxml-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.2</version>
        </dependency>
		<!--xercesImpl-->
        <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>2.12.0</version>
        </dependency>

二、代码示例

package com.yl.excel;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.json.JSON;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.sax.Excel03SaxReader;
import cn.hutool.poi.excel.sax.Excel07SaxReader;
import cn.hutool.poi.excel.sax.handler.RowHandler;
import cn.hutool.poi.word.Word07Writer;
import com.yl.entity.User;
import lombok.extern.slf4j.Slf4j;

import java.awt.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * 描述:  Excel大数据生成-BigExcelWriter
 * 对于大量数据输出,采用ExcelWriter容易引起内存溢出,因此有了BigExcelWriter,使用方法与ExcelWriter完全一致
 *
 * @author: yanglin
 * @Date: 2020-07-13-16:19
 * @Version: 1.0
 */
@Slf4j
public class HutoolExcel {

    /**
     * ExcelUtil Excel工具类,读取的快捷方法都被封装于此
     * ExcelReader Excel读取器,Excel读取的封装,可以直接构造后使用。
     * ExcelWriter Excel生成并写出器,Excel写出的封装(写出到流或者文件),可以直接构造后使用。
     */

    /**
     * 创建Excel
     * @param excelName
     */
    public static void testCreateExcel(String excelName){
        List<?> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd", DateUtil.date(), 3.22676575765);
        List<?> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1", DateUtil.date(), 250.7676);
        List<?> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2", DateUtil.date(), 0.111);
        List<?> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3", DateUtil.date(), 35);
        List<?> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4", DateUtil.date(), 28.00);

        List<List<?>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);
        BigExcelWriter writer= ExcelUtil.getBigWriter(excelName);
        // 一次性写出内容,使用默认样式
        writer.write(rows);
        // 关闭writer,释放内存
        writer.close();
    }

    /**
     * 创建Word
     * @param wordName
     */
    public static void testCreateWord(String wordName){
        Word07Writer writer = new Word07Writer();
        // 添加段落(标题)
        writer.addText(new Font("方正小标宋简体", Font.PLAIN, 22), "我是第一部分", "我是第二部分");
        // 添加段落(正文)
        writer.addText(new Font("宋体", Font.PLAIN, 22), "我是正文第一部分", "我是正文第二部分");
        // 写出到文件
        writer.flush(FileUtil.file(wordName));
        // 关闭
        writer.close();
    }

    /**
     * Excel读取-ExcelReader
     * @param excelName
     */
    public static void readExcel(String excelName){
        // 读取Excel中所有行和列,都用列表表示
        ExcelReader reader = ExcelUtil.getReader(excelName);
        List<List<Object>> readAll = reader.read();

        // 读取为Map列表,默认第一行为标题行,Map中的key为标题,value为标题对应的单元格值。
        ExcelReader readerMap = ExcelUtil.getReader(excelName);
        List<Map<String,Object>> readMapAll = readerMap.readAll();

        // 读取为Bean列表,Bean中的字段名为标题,字段值为标题对应的单元格值。
        ExcelReader readerClass = ExcelUtil.getReader("d:/aaa.xlsx");
        List<Object> all = readerClass.readAll(Object.class);
    }

    /**
     * 流方式读取Excel2003-Excel03SaxReader
     * Excel03SaxReader只支持Excel2003格式的Sax读取。
     * 在标准的ExcelReader中,如果数据量较大,读取Excel会非常缓慢,并有可能造成内存溢出。因此针对大数据量的Excel,Hutool封装了event模式的读取方式。
     * @param excelName
     */
    public static void readStream2003Excel(String excelName){
        // ExcelUtil快速读取
        ExcelUtil.read03BySax(excelName, 1, createRowHandler());

        // 构建对象读取
        Excel03SaxReader reader = new Excel03SaxReader(createRowHandler());
        // reader方法的第二个参数是sheet的序号,-1表示读取所有sheet,0表示第一个sheet,依此类推。
        reader.read(excelName, 0);
    }

    /**
     * 首先我们实现一下RowHandler接口,这个接口是Sax读取的核心,通过实现handle方法编写我们要对每行数据的操作方式
     * (比如按照行入库,入List或者写出到文件等)
     * @return
     */
    private static RowHandler createRowHandler() {
        return new RowHandler() {
            @Override
            public void handle(int sheetIndex, int rowIndex, List<Object> rowlist) {
                log.info("[{}] [{}] {}", sheetIndex, rowIndex, rowlist);
            }
        };
    }

    /**
     * 流方式读取Excel2007-Excel07SaxReader
     * 在标准的ExcelReader中,如果数据量较大,读取Excel会非常缓慢,并有可能造成内存溢出。因此针对大数据量的Excel,Hutool封装了Sax模式的读取方式。
     * Excel07SaxReader只支持Excel2007格式的Sax读取。
     * @param excelName
     */
    public static void readStream2007Exce(String excelName){
        // ExcelUtil快速读取
        ExcelUtil.read07BySax(excelName, 0, createRowHandler());

        // 构建对象读取
        Excel07SaxReader reader = new Excel07SaxReader(createRowHandler());
        reader.read(excelName, 0);
    }

    /**
     * Excel生成-ExcelWriter
     * Hutool将Excel写出封装为ExcelWriter,原理为包装了Workbook对象,每次调用merge(合并单元格)或者
     * write(写出数据)方法后只是将数据写入到Workbook,并不写出文件,只有调用flush或者close方法后才会真正写出文件。
     * 由于机制原因,在写出结束后需要关闭ExcelWriter对象,调用close方法即可关闭,此时才会释放Workbook对象资源,
     * 否则带有数据的Workbook一直会常驻内存。
     *
     * @param toExcelName
     */
    public static void createExcel(String toExcelName){
        // 1. 将行列对象写出到Excel
        List<String> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd");
        List<String> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1");
        List<String> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2");
        List<String> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3");
        List<String> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4");

        List<List<String>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);

        //通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getWriter(toExcelName);
        //通过构造方法创建writer
        //ExcelWriter writer = new ExcelWriter("d:/writeTest.xls");

        //跳过当前行,既第一行,非必须,在此演示用
        writer.passCurrentRow();

        //合并单元格后的标题行,使用默认标题样式
        writer.merge(row1.size() - 1, "测试标题");
        //一次性写出内容,强制输出标题
        writer.write(rows, true);
        //关闭writer,释放内存
        writer.close();
    }

    /**
     * 测试excel导出百万条记录
     * @param data
     * @param toExcelName
     */
    public static void createExcelData(List<List<?>> data, String toExcelName){
        //通过工具类创建writer
        BigExcelWriter writer= ExcelUtil.getBigWriter(toExcelName);
        // 一次性写出内容,使用默认样式
        writer.write(data);
        // 关闭writer,释放内存
        writer.close();
    }

    public static List<List<?>> createMillionExcelData(){
        // 测试excel导出百万条记录 创建测试数据
        User user;
        List<List<?>> users = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            user = User.builder().id(i).name("东芝王哥"+i).sex("Y").age(10+i)
                    .love("吃饭睡觉大殴打"+i).eat("吃大虾"+i).run("徒步5000"+i+"米")
                    .idCard("42595956874412454554"+i).birthday(LocalDateTime.now())
                    .securityCode("1245"+i).account("adminqwe"+i).password("qwer"+i)
                    .remark("z这是一个爱好学习、天天向上的bgm"+i)
                    .build();
            // log.info("第 {} 个user::{}", i, user.toString());
            String[] userStr = user.toString().substring(
                    user.toString().indexOf("(") + 1, user.toString().lastIndexOf(")")).split(",");
            List<String> userStrs = CollUtil.newArrayList(userStr);
            List<String> userStrNews = new ArrayList<>();

            if (i == 0) {
                userStrs.forEach( u -> {
                    u = u.split("=")[0];
                    userStrNews.add(u);
                });
            }else{
                userStrs.forEach( u -> {
                    u = u.split("=")[1];
                    userStrNews.add(u);
                });
            }
            users.add(userStrNews);
        }
        return users;
    }

    /**
     * 并发创建测试数据
     *
     * @param toExcelName
     * @throws ExecutionException
     * @throws InterruptedException
     */
    public static void createMillionExcelDataFuture(String toExcelName) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        log.info("createMillionExcelDataFuture start {} ", start);
        // 定时多个FutureTask生成数据
        FutureTask<List<List<?>>> taskOne = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                return createMillionExcelData();
            }
        });

        FutureTask<List<List<?>>> taskTwo = new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                return createMillionExcelData();
            }
        });

        Thread thread1 = new Thread(taskOne);
        thread1.start();
        Thread thread2 = new Thread(taskTwo);
        thread2.start();

        List<List<?>> data = new ArrayList<>();

        // 得到FutureTask生成的结果 阻塞
        List<List<?>> oneList = taskOne.get();
        data.addAll(oneList);
        List<List<?>> twoList = taskTwo.get();
        data.addAll(twoList);

        // 设置表头

        // 生成excel
        //通过工具类创建writer
        BigExcelWriter writer= ExcelUtil.getBigWriter(toExcelName);
        // 一次性写出内容,使用默认样式
        writer.write(data);
        // 关闭writer,释放内存
        writer.close();

        log.info("createMillionExcelDataFuture end 耗时 {}", System.currentTimeMillis() - start);
    }

    public static void main(String[] args) {
        String proDir = System.getProperty("user.dir") +"/springboot-jacob/src/main/resources";
        String excelName = proDir + "/excel/"+System.currentTimeMillis()+"test.xlsx";
        /*String wordName = proDir + "/word/"+System.currentTimeMillis()+"test.doc";
        log.info("创建一个excel.................");
        testCreateExcel(excelName);
        log.info("创建一个word.................");
        testCreateWord(excelName);*/

        try {
            createMillionExcelDataFuture(excelName);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

参考Hutool文档 https://www.hutool.cn/docs/#/poi/Excel%E5%A4%A7%E6%95%B0%E6%8D%AE%E7%94%9F%E6%88%90-BigExcelWriter

以上 

猜你喜欢

转载自blog.csdn.net/qq_35731570/article/details/109721406