poi读取超大excel内存溢出问题

读取excel代码通常采用以下这种方式将文件流转为XSSFWorkbook,但超大数据量excel会导致内存溢出。

  XSSFWorkbook sheets = new XSSFWorkbook(inputStream);

可使用流式处理组件xlsx-streamer解决内存溢出问题,pom引入jar

<dependency>
    <groupId>com.monitorjbl</groupId>
    <artifactId>xlsx-streamer</artifactId>
    <version>2.1.0</version>
</dependency>

 改为

Workbook wk=StreamingReader.builder()
                    .rowCacheSize(100)  //缓存到内存中的行数,默认是10
                    .bufferSize(4096)  //读取资源时,缓存到内存的字节大小,默认是1024
                    .open(inputStream)

使用示例 

 public List<List<Object>> getDataList(String path,String fileName) {
        File file = null;
        List<List<Object>> objectList = new ArrayList<>();
        try {
            file = ResourceUtils.getFile(path + fileName);
            // 获取文件输入流
            InputStream inputStream = new FileInputStream(file);
            log.info("开始读取excel数据");
            Workbook wk = ExcelUtil.getWorkbookByInputStream(inputStream);
            log.info("读取excel数据结束");
            //获取sheet
            Sheet sheet = wk.getSheetAt(0);
            //获取行数
            int i = 0;
            Integer head = 0;
            for (Row row : sheet) {
                List<Object> list = new ArrayList<>();
                i++;
                //遍历所有的列
                if (i == 1) {
                    for (int j = 0; j < row.getLastCellNum(); j++) {
                        head++;
                    }
                }
                //从第2行开始取数据,如果是空值就赋空字符
                if (i > 1) {
                    for (int k = 0; k < head; k++) {
                            if (row.getCell(k) != null) {
                                //ExcelUtil.getCellValue单元格格式转换
                                list.add(ExcelUtil.getCellValue(row.getCell(k)));
                            }else{
                                list.add(" ");
                            }
                    }
                    objectList.add(list);
                }
            }

        } catch (Exception e) {
            log.error("读取excel文件失败{}", JSON.toJSONString(e));
         
        }
        return objectList;
    }

import cn.hutool.core.date.DateUtil;
import com.monitorjbl.xlsx.StreamingReader;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.ss.usermodel.*;
import org.springframework.util.StringUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.Iterator;


/**
 * @Author: ws
 * @CreateTime: 2022-10-28  09:51
 * @Version: 1.0
 */
@Slf4j
public class ExcelUtil {

    /**
     * 数据格式化工具类
     */
    private static final DataFormatter DF = new DataFormatter();
    public static String getCellValue(Cell cell) {
        try {
            if (cell == null) {
                return " ";
            }

            switch (cell.getCellTypeEnum()) {
                case NUMERIC: // 数字
                    if (HSSFDateUtil.isCellDateFormatted(cell)) {
                        Date date = cell.getDateCellValue();
                        return DateUtil.format(date, "yyyy-MM-dd HH:mm:ss");
                    }
                    DecimalFormat df = new DecimalFormat("0");//处理科学计数法
                    String val = df.format(cell.getNumericCellValue());
                    return val;
                case STRING: // 字符串
                    return cell.getStringCellValue();
                case BOOLEAN: // Boolean
                    return String.valueOf(cell.getBooleanCellValue());
                case FORMULA: // 公式
                    return String.valueOf(cell.getCellFormula());
                case BLANK: // 空值
                    return null;
                case ERROR: // 故障
                    return "非法字符";
                default:
                    return "未知类型   ";
            }
        } catch (Exception e) {
            System.out.println(e);
        }
        return " ";
    }
    /**
     * 获取指定sheet表单中除去空行的实际数据条数
     * @param wb workbook
     * @return 去空行后的数据总条数
     */
    public static int getRealRowCountByWorkbook(Workbook wb) {
        Sheet sheet = wb.getSheetAt(0);
        int count = 0;
        for (Row row : sheet) {
            int j = 0, nullCellCount = 0;
            for (Iterator<Cell> ite = row.iterator(); ite.hasNext(); j++) {
                Cell cell = ite.next();
                if (cell == null || StringUtils.isEmpty(DF.formatCellValue(cell))) {
                    nullCellCount ++;
                }
            }
            if (nullCellCount >= j) {
                return count;
            }
            count++;
        }
        return count;
    }
    /**
     * 根据工作簿对象获取指定sheet表单中除去标题行后的总条数(可能包含空行),默认读取第一个sheet
     * @param wb 工作簿对象
     * @return 指定sheet中除去首行标题行后的物理数据条数(可能包含空行)
     */
    public static int getPhysicalRowCountByWorkbook(Workbook wb) {
        Sheet sheet = wb.getSheetAt(0);
        return sheet.getLastRowNum();
    }

    /**
     * 根据输入流和指定要读取的sheet下标值来获取表格的物理数据总条数(可能包含空行)
     * eg: int count = ExcelReader.getPhysicalRowCountByInputStream(inputStream); // 获取excel文件中第一个表单的物理数据总行数(有可能包含空行)
     * @param inputStream excel文件输入流
     * @return 可能包含空行的物理数据总条数
     */
    public static int getPhysicalRowCountByInputStream(InputStream inputStream) {
        try (Workbook wb = getWorkbookByInputStream(inputStream)) {
            return getPhysicalRowCountByWorkbook(wb);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }



    /**
     * <p>
     * 	通过输入流创建workbook,单独调用记得关闭流(上面通过try()的方式会自动关闭流,因为他们实现了AutoCloseble)
     * </p>
     * @param inputStream excel文件流
     * @return Workbook对象
     */
    public static Workbook getWorkbookByInputStream(InputStream inputStream) {
        try {
            return StreamingReader.builder()
                    .rowCacheSize(100)  //缓存到内存中的行数,默认是10
                    .bufferSize(4096)  //读取资源时,缓存到内存的字节大小,默认是1024
                    .open(inputStream);//打开资源,必须,可以是InputStream或者是File,注意:只能打开XLSX格式的文件
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * <p>
     * 	通过文件创建workbook,单独调用记得关闭流
     * </p>
     * @param file excel文件
     * @return Workbook对象
     */
    public static Workbook getWorkbookByFile(File file) {
        try (InputStream inputStream = new FileInputStream(file)) {
            return getWorkbookByInputStream(inputStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

  • 0
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用POI读取Excel文件时,如果遇到大量的空行,可能会导致内存溢出问题。这是因为POI读取Excel文件时会将整个文件的内容全部加载到内存中,而空行并没有实际的数据内容,但仍然会占用内存空间。 解决这个问题的方法有以下几种: 1. 使用逐行读取的方式:通过POI提供的API,可以逐行读取Excel文件的内容,而不是将整个文件加载到内存中。这样可以有效地避免空行占用过多的内存空间。 2. 添加筛选条件:在读取Excel文件时,可以添加筛选条件,只读取有效的数据行,而忽略空行。可以通过判断某一行是否为空行的方式,进行过滤。 3. 设置最大行数限制:可以设置最大行数的限制,当达到设定的最大行数时,停止继续读取Excel文件。这样可以避免读取过多的空行,从而减少内存占用。 4. 对大文件进行分块处理:如果Excel文件太大,无法完全加载到内存中,可以将文件进行分块处理,每次读取一部分内容,然后进行处理,这样可以避免一次性读取整个文件导致内存溢出。 需要注意的是,以上方法仅仅是针对空行导致的内存溢出问题,如果Excel文件本身非常大,仍然可能会出现内存溢出的情况。为了避免此类问题,可以考虑采取其他的解决方案,例如使用数据库进行存储和查询,或者使用分布式处理来处理大规模的数据。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今朝花落悲颜色

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值