处理大量数据的Excel报表

处理大量数据的Excel报表

1. 报表导出

1.1 需求

我们需要导出包含数百万行数据的Excel报表,同时确保在处理大数据量时不会出现内存溢出问题。

1.2 解决方案

1.2.1 思路

传统的Excel导出方式通常会将所有数据一次性加载到内存中,这可能导致内存占用激增。为了解决这个问题,我们采用了Apache POI库中的SXSSFWorkbook,它可以用于处理大规模数据量的Excel报表导出。

1.2.2 原理

SXSSFWorkbook允许将数据对象分块写入磁盘,以避免内存溢出。当内存中的对象数量达到指定值时,数据将被写入磁盘(以XML格式存储),然后从内存中释放。

1.3 代码示例

在代码中,首先构建需要导出的数据,然后创建一个SXSSFWorkbook对象。接着逐行创建表格数据并将其写入Excel文件。最后,设置响应头,以便用户下载生成的Excel文件。

java 复制代码
// 构建数据
List<TestDataResult> dataList = TestService.findByReport(testId, month + "%");

// 创建工作簿
SXSSFWorkbook workbook = new SXSSFWorkbook();

// 创建工作表和表头
String[] columnHeaders = {"编号", "姓名", "手机", /*...其他列名...*/};
Sheet sheet = workbook.createSheet();
Row headerRow = sheet.createRow(0);

for (int i = 0; i < columnHeaders.length; i++) {
    Cell cell = headerRow.createCell(i);
    cell.setCellValue(columnHeaders[i]);
}

// 逐行写入数据
for (int i = 0; i < dataList.size(); i++) {
    Row dataRow = sheet.createRow(i + 1);
    TestDataResult report = dataList.get(i);
    // 将数据写入单元格
    // ...
}

// 导出Excel文件
// 设置响应头
String fileName = URLEncoder.encode(month + "人员信息.xlsx", "UTF-8");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);

// 将工作簿写入响应流
workbook.write(response.getOutputStream());

2. 报表导入

2.1 需求

在处理大量数据时,我们需要能够从Excel文件中导入数据,同时避免内存溢出问题。

2.2 解决方案

2.2.1 思路

传统的Excel读取方式通常将整个文件加载到内存中,然后逐一解析每个单元格。对于大数据量的Excel文件,这可能会导致内存不足或OOM异常。为了解决这个问题,我们采用了事件驱动的SAX解析方式,它逐行扫描文档并逐行解析,无需将整个文件存储在内存中。

2.2.2 原理

在事件模式下,Excel文件会被逐行扫描和解析,只有在读取数据时才会加载到内存中,因此适用于大型文档。我们使用自定义的SheetHandler来处理每一行的数据,并将其封装成实体对象。

2.3 代码示例

PoiEntity类

java 复制代码
public class PoiEntity {
    private String id;
    private String breast;
    private String adipocytes;
    private String negative;
    private String staining;
    private String supportive;

    // 各属性的getters和setters
}

自定义处理器SheetHandler类

java 复制代码
import cn.itcast.poi.entity.PoiEntity;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;

public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {

    private PoiEntity entity;

    @Override
    public void startRow(int i) {
        if (i > 0) {
            entity = new PoiEntity();
        }
    }

    @Override
    public void endRow(int i) {
        // 使用实体对象进行业务操作,例如存储到数据库或进行其他处理
        System.out.println(entity);
    }

    @Override
    public void cell(String cellReference, String value, XSSFComment xssfComment) {
        if (entity != null) {
            String col = cellReference.substring(0, 1);
            switch (col) {
                case "A":
                    entity.setId(value);
                    break;
                case "B":
                    entity.setBreast(value);
                    break;
                case "C":
                    entity.setAdipocytes(value);
                    break;
                case "D":
                    entity.setNegative(value);
                    break;
                case "E":
                    entity.setStaining(value);
                    break;
                case "F":
                    entity.setSupportive(value);
                    break;
                default:
                    break;
            }
        }
    }
}

ExcelParser类

java 复制代码
public class ExcelParser {
    public void parse(String path) throws Exception {
        // 打开Excel文件
        OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);

        try {
            // 创建XSSFReader对象
            XSSFReader reader = new XSSFReader(pkg);

            // 获取SharedStringsTable对象
            SharedStringsTable sst = reader.getSharedStringsTable();

            // 获取StylesTable对象
            StylesTable styles = reader.getStylesTable();

            // 创建SAX的XmlReader对象
            XMLReader parser = XMLReaderFactory.createXMLReader();

            // 设置事件处理器
            parser.setContentHandler(new XSSFSheetXMLHandler(styles, sst, new SheetHandler(), false));

            // 获取所有Sheet并逐行读取
            XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator) reader.getSheetsData();
            while (sheets.hasNext()) {
                InputStream sheetStream = sheets.next();
                InputSource sheetSource = new InputSource(sheetStream);
                try {
                    // 开始解析
                    parser.parse(sheetSource);
                } finally {
                    sheetStream.close();
                }
            }
        } finally {
            pkg.close();
        }
    }
}
相关推荐
houyhea12 分钟前
从香港竹脚手架到前端脚手架:那些"借来"的发展智慧与安全警示
前端
_院长大人_19 分钟前
MyBatis Plus 分批查询优化实战:优雅地解决 IN 参数过多问题(实操)
java·mybatis
哈哈~haha34 分钟前
Step 14: Custom CSS and Theme Colors 自定义CSS类
前端·css·ui5
Ndmzi41 分钟前
Matlab编程技巧:自定义Simulink菜单(理解补充)
前端·javascript·python
C雨后彩虹1 小时前
机器人活动区域
java·数据结构·算法·华为·面试
我命由我123451 小时前
VSCode - VSCode 修改文件树缩进
前端·ide·vscode·前端框架·编辑器·html·js
a3158238061 小时前
Android Framework开发知识点整理
android·java·linux·服务器·framework·android源码开发
SoaringHeart2 小时前
Flutter组件封装:验证码倒计时按钮 TimerButton
前端·flutter
毕设源码-朱学姐2 小时前
【开题答辩全过程】以 个人健康管理系统为例,包含答辩的问题和答案
java·spring boot