百万数据切片导出工具类

package com.*.common.core.utils.poi;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.Stack;
import java.util.stream.Collectors;

/**
 * Ⓕ大数据量excel 分表切片导出
 *
 * @param <T> 操作类 10w+
 */
public class ExcelBigDataUtil<T> implements AutoCloseable {

    private ExcelWriter excelWriter;

    // 当前操作sheet
    private WriteSheet sheets;
    // 当前sheet index 0开始
    private Integer index = 0;
    // sheet分表名
    private String[] sheetNames = null;
    // 单次最大写入数量
    private final static Integer MAX_IN_SIZE = Integer.valueOf(5000);
    // 操作对象类型
    private Class<T> target;
    // 文件完成状态
    public Boolean isCompeted = false;
    // 缓存文件夹
    private final static String bigExcelPath = "D:\\charts";
    // 磁盘缓存路径
    private String path;
    // 调用加载方式 true(进程内存 + 磁盘)
    private Boolean isCash;

    public ExcelBigDataUtil(String[] sheetNames, Class<T> target) {
        this.sheetNames = sheetNames;
        this.target = target;
    }

    /**
     * 初始配置(构造器)
     *
     * @param response 响应体
     * @param target 操作类(含注解)
     * @param fileName 磁盘文件名(缓存通过此)
     * @param isCash false 拒绝采用之前统计文件
     * @return 构建对象
     * @throws IOException 磁盘读取失败
     */
    public static <T> ExcelBigDataUtil<T> config(HttpServletResponse response, Class<T> target,
                                                 String fileName, Boolean isCash) throws IOException {
        return config(response,target,null,fileName,null,isCash);
    }

    /**
     * 初始配置(构造器)
     *
     * @param response 响应体
     * @param target 操作类(含注解)
     * @param excelType 文件类型
     * @param fileName 磁盘文件名(缓存通过此)
     * @param sheetNames 分表名(默认’分表+index')
     * @param isCash false 拒绝采用之前的统计文件
     * @return 构建对象
     * @throws IOException 磁盘读取失败
     */
    public static <T> ExcelBigDataUtil<T> config(HttpServletResponse response, Class<T> target, ExcelTypeEnum excelType,
                                                 String fileName, String[] sheetNames, Boolean isCash) throws IOException
    {
        ExcelBigDataUtil util;
        if(isCash)
        {
            Assert.notNull(ExcelBigDataUtil.bigExcelPath, "路径属性未注入");
            File dir = new File(bigExcelPath);
            if (!dir.canWrite()) throw new RuntimeException("文件路径无权限访问");
            if (dir.isDirectory()) dir.mkdirs();

            util = new ExcelBigDataUtil<>(sheetNames, target);
            util.isCash = true;

            // 是否从磁盘读取到文件
            String path = ExcelBigDataUtil.bigExcelPath + "\\" + fileName + ".xlsx";
            util.path = path;
            util.readByDisk(response,false);

            // 新生成文档操作对象
            if(!util.isCompeted){
                if(excelType == null) excelType = ExcelTypeEnum.XLSX;
                // 写入磁盘
                util.excelWriter = EasyExcel.write(path, target).excelType(excelType).build();
            }

            // 删除空文件
            File file = new File(path);
            if (file.length() == 0) file.delete();
        }
        else
        {
            // 直接加载进响应体
            util = new ExcelBigDataUtil<>(sheetNames, target);
            util.isCash = false;

            // 新生成文档操作对象
            if(excelType == null) excelType = ExcelTypeEnum.XLSX;
            util.excelWriter = EasyExcel.write(response.getOutputStream()).excelType(excelType).build();
        }

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");

        return util;
    }

    /**
     * 数据分次提交
     *
     * @param response 响应体
     * @param data 切片数据
     * @param isNext 是否换表
     * @param isFinish 是否结束
     * @throws IOException 磁盘读取失败
     */
    public void submit(HttpServletResponse response, List<T> data, Boolean isNext, Boolean isFinish) throws IOException {

        Assert.notNull(excelWriter,"使用需配置");
        Assert.notNull(data,"空数据源");
        Assert.notNull(data.size() > MAX_IN_SIZE ? null : true,"单次写入受限");

        data = data.stream().filter(var -> !ObjectUtils.isEmpty(var)).collect(Collectors.toList());
        String sheetName = sheetNames != null ? sheetNames[index] : ("分表" + (index + 1));

        // 分表操作
        WriteSheet point;

        // 接上表
        point = next(null);

        // 换表
        if(isNext || sheets == null){
            ++index;
            point = EasyExcel.writerSheet(index,sheetName).head(target).build();
            next(point);
        }

        // 写入数据
        excelWriter.write(data, point);
        data = null;

        // 最终处理
        if(isFinish) {
            this.readByDisk(response,true);
        }
    }


    /**
     * 操作分表栈
     *
     * @param sheet 入栈元素
     * @return 出栈元素
     */
    private WriteSheet next(WriteSheet sheet){
        // 换表
        if(sheet != null) sheets = sheet;
        // 不换表
        if(sheets == null && sheet == null) return null;
        return sheets;
    }

    /**
     * 读取磁盘文件加载进响应体
     *
     * @param response
     * @throws IOException
     */
    private void readByDisk(HttpServletResponse response,Boolean isFinish) throws IOException{

        if(isFinish != null && isFinish) excelWriter.finish();
        if(!isCash || isCompeted) return;
        if(path == null) return;

        // 磁盘中存在文件
        File file = new File(path);
        if(!file.isFile() || file.length() == 0)  return;

        FileInputStream fileInputStream = new FileInputStream(file);

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setContentLength((int) file.length());

        // 缓冲区读取
        BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
        byte[] data = new byte[bufferedInputStream.available()];
        bufferedInputStream.read(data);

        // 写入响应体
        OutputStream outputStream = response.getOutputStream();
        outputStream.write(data);

        // 关闭新建流
        bufferedInputStream.close();
        outputStream.flush();
        outputStream.close();

        this.isCompeted = true;
    }

    @Override
    public void close() {
        if(sheets != null) sheets = null;
        if(excelWriter != null) excelWriter.finish();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值