一、背景描述

在上传大数据量的excel文档(.xlsx)时,报错:

Tried to allocate an array of length 260,008,239, but the maximum length for this record type is 100,000,000
if the file is not corrupt or large, please open an issue on bugzilla to request
increasing the maximum allowable size for this record type.
As a temporary workaround, consider setting a higher override value with lOUtils.setByteArrayMaxOverride()

缩小excel的大小,报错

<!DOCTYPEhtml><html> <head>
<title>Error</title> <style> body  width:
35em; margin: 0 auto; font-family: Tahoma
Verdana, Arial, sans-serif;} </style> </head>
<bodv> <h1>An error occurred.</h1>
<p>Sorry, the page you are looking for is
currently unavailable.<br/> Please try again
later.</p> <p>lf you are the system
administrator of this resource then you should
check the <a
href="http://nginx.org/r/error log">error
log</a> for details.</p> <p> <em>Faithfully
yours, nginx.</em></p> </body> </html>

二、报错原因和解决方案

1、Tried to allocate an array of length 260,008,239

报错代码:

MultipartFile file;
InputStream is = file.getInputStream();
// 这一行代码报错
Workbook wb = new XSSFWorkbook(is);

原因:

尝试分配一个长度为260,008,239的数组,但是该记录类型的最大长度为100,000,000所导致的。

解决方案1:

直接扩大允许的最大长度。

MultipartFile file;
InputStream is = file.getInputStream();
// 添加这行代码,根据你的报错给,比如我这里是260,008,239,那就比这个大
IOUtils.setByteArrayMaxOverride(300000000);
Workbook wb = new XSSFWorkbook(is);

解决方案2:

使用流

MultipartFile file;
InputStream is = file.getInputStream();
// 这个时候wb的类是StreamingWorkbook
Workbook wb = StreamingReader.builder()
	.rowCacheSize(100)
	.bufferSize(1024 * 4)
	.open(is);

2、Sorry, the page you are looking for is currently unavailable.

原因:

这是Nginx服务器的报错,是因为后端接口时间长时间没返回结果,服务器自动连接超时。

解决方案:

优化代码结构。

三、优化代码

项目上使用的 XSSFWorkbook 来处理 .xlsx文件。
需求:
将.xlsx 的每一个页签单独生成一个新的excel文件,并上传文件。
消耗内存和时间最长的:
将 原文件的每一个sheet页的值复制给新的 Workbook的sheet。
解决方案:
都使用流来进行。
具体步骤:
1、使用 StreamingWorkbook 来接收 原excel文件
2、使用 SXSSFWorkbook 来接收 原excel的每一个页签
代码:

MultipartFile file;
boolean excelFlag = ExcelUtils.isExcelFormat(fileParentName);
InputStream is = file.getInputStream();
try (Workbook wb = (excelFlag ? new HSSFWorkbook(is) :
        StreamingReader.builder().rowCacheSize(100).bufferSize(1024 * 4).open(is))) {
    int numberOfSheets = wb.getNumberOfSheets();
    for (int i = 0; i < numberOfSheets; i++) {
        Sheet sheet = wb.getSheetAt(i);
        // 设置sheet页名
        String sheetName = this.getsheetName();
        try (Workbook sheetWorkbook = new SXSSFWorkbook()) {
            Sheet newSheet = sheetWorkbook.createSheet(sheetName);
            this.copySheet(sheet, newSheet);
        }
    }
}
private void copySheet(Sheet sourceSheet, Sheet targetSheet) {
    sourceSheet.forEach(row -> {
        Row targetRow = targetSheet.createRow(row.getRowNum());
        // 使用默认高度
        targetSheet.getRow(row.getRowNum()).setHeight(targetSheet.getDefaultRowHeight());
        row.forEach(cell -> {
            targetRow.createCell(cell.getColumnIndex());
			// todo:进行格式转换会报错,这个后续需要修改
			// 是调用这个方法报错的,org.apache.poi.ss.usermodel.DataFormatter#isDate1904
            // String cellValue = new DataFormatter().formatCellValue(cell);
            targetRow.getCell(cell.getColumnIndex()).setCellValue(cell.getStringCellValue());
        });
    });
}

优化结果:
内存使用:几个G–>几百Mb
消耗时间:一百多秒 -->二三十秒

Logo

永洪科技,致力于打造全球领先的数据技术厂商,具备从数据应用方案咨询、BI、AIGC智能分析、数字孪生、数据资产、数据治理、数据实施的端到端大数据价值服务能力。

更多推荐