apache-poi导出数据到excel(SXSSF)

得意时要看淡,失意时要看开。不论得意失意,切莫大意;不论成功失败,切莫止步。志得意满时,需要的是淡然,给自己留一条退路;失意落魄时,需要的是泰然,给自己觅一条出路apache-poi导出数据到excel(SXSSF),希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

前言

最近公司有一个云服务涉及到了将设备数据导出到excel并从浏览器弹出下载的需求,项目原先使用的是poi-3.17版本的HSSFWorkbook 实现导出的。但是最近数据量增大频频出问题,高于5W行的导出几乎必然报错。领导交给我来跟踪,以前也了解过但一知半解,这次好好整理总结一下,有问题欢迎提出,共同进步。
在这里插入图片描述

一. POI处理excel简单介绍

首先,apache-poi官方提供了HSSF、XSSF、SXSSF三种方式来操作Excel。
HSSF:操作 excel97-2003版本,扩展名为 .xls ;
XSSF:操作 excel2007及更高版本,扩展名为 .xlsx。
SXSSF:是在XSSF的基础上,从poi-3.8版本后开始出现的处理内存占用的一种高效导出excel方式。

另外,了解以下知识点:
① 支持的行数、列数

excel2003版本,一个shee最大行数为65536,最大列数256。
excel2007版本开始,一个sheet最大行数1048576,最大列数16384。

② 文件大小

.xlsx格式的文件比 .xls格式的压缩率高,即数据量一样得情况下,.xlsx的文件更小。

③ 兼容性

excel2003版本不支持打开 .xlsx文件;
excel2007开始的版本向下兼容,支持打开 .xls文件。

④ 三者各自对应的类
HSSF对应: > HSSFWorkbook、HSSFSheet、HSSFRow、HSSFCell……
XSSF对应:> XSSFWorkbook、XSSFSheet、XSSFRow、XSSFCell……
SXSSF对应:> SXSSFWorkbook、SXSSFSheet、SXSSFRow、SXSSFCell……

二. poi导出excel的代码

这里直接放上 SXSSF导出的代码:

// 新的导出方法(使用SXSSFWorkbook导出)
    public void excelExport() {
        // 导出前设置session中exportedFlag任意值,导出后清空
        super.getSession().setAttribute("exportedFlag", "false");

        SXSSFWorkbook workbook = null;
        workbook = new SXSSFWorkbook(100);//默认SXSSFWorkbook只会保留100条数据在内存中,避免内存溢出,旧的会刷新到磁盘中
        workbook.setCompressTempFiles(true); //压缩临时文件,很重要,否则磁盘很快就会被写满
        SXSSFSheet sheet = workbook.createSheet("设备信息");
        try {
            String genDir = getRequest().getSession().getServletContext().getRealPath("/excel_gen");
            logger.info("genDir: " + genDir);

            String filename = UUID.randomUUID().toString() + ".xlsx";
               新建行头标题
            SXSSFRow headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("id");
            headerRow.createCell(1).setCellValue("companyName ");
            headerRow.createCell(2).setCellValue("Device_Model");//设备型号
            headerRow.createCell(3).setCellValue("System_version");
            headerRow.createCell(4).setCellValue("FirmWare_version");
            ...
            
            List<String> deviceIds = new ArrayList<String>();  // 循环写入数据
            int count = 0;  // 统计每次循环写入行数 , 累加创建Row
            for(int k=0;;k++) { // 此处k无边界条件,因为未知数据量,当查询rows为空时break退出循环
                logger.info("进入分页查询for循环:k = "+k);
                DeviceForm form = (DeviceForm) session.get("_device_query_form");
                form.setPage(k+1);
                form.setRows(10000);//
                String strTimeZone = getForm().getTimeZone();
                int timezone = Integer.parseInt(strTimeZone);
                long startTime1 = System.currentTimeMillis();
                JSONObject jsonObject = JSONObject.parseObject(getService().query(form)); // 字符串转对象
                long endTime1 = System.currentTimeMillis();

                JSONArray rows = jsonObject.getJSONArray("rows");

                if (rows.isEmpty()) {
                    logger.info("查询rows为空,结束分页查询for循环——>excelExport k=" +  k );
                    break;
                }else{
                    String companyName = "";
                    String deviceModel = "";
                    String operatingSystemInfo = "";
                    String firmwareVersion = "";
                    String id = "";
                    
                    for (int i = 0; i < rows.size(); i++) {
                        JSONObject row = rows.getJSONObject(i);
                        try {

                            SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
                            companyName = row.containsKey("companyName") ? row.getString("companyName") : "";
                            deviceModel = row.containsKey("deviceModel") ? row.getString("deviceModel") : "";
                            firmwareVersion = row.containsKey("firmwareVersion") ? row.getString("firmwareVersion") : "";
                            id = row.getString("id");
                            operatingSystemInfo = row.containsKey("operatingSystemInfo") ? row.getString("operatingSystemInfo") : "";

                        } catch (Exception e) {              
                            log.error(e);
                        }

                        SXSSFRow excelRow = sheet.createRow(i + 1 + count);
                        excelRow.createCell(0).setCellValue(id);
                        excelRow.createCell(1).setCellValue(companyName );
                        excelRow.createCell(2).setCellValue(deviceModel);
                        excelRow.createCell(3).setCellValue(operatingSystemInfo);
                        excelRow.createCell(4).setCellValue(firmwareVersion);  // 固件版本
                        
                        deviceIds.add(id);
                    }
                    count += rows.size();
                }
                logger.info("当前页循环结束,after generate excel data , 数据总行数为: "+ count);

            }
            // 设置响应头为——>下载文件
            HttpServletResponse response = ServletActionContext.getResponse(); // Content-Disposition文件下载的方式;text/plain输出到浏览器
            response.setHeader("Content-Disposition", "attachment;filename=" + new String(filename.getBytes(StandardCharsets.UTF_8)));
            response.addHeader("Pragma", "no-cache");
            response.addHeader("Cache-Control", "no-cache");
            response.setContentType("application/octet-stream;charset=UTF-8");
            ServletOutputStream out = response.getOutputStream(); // 表格对象注入Stream流中
            try {
                logger.info("一次性写入文件...");
                workbook.write(out);  // 同一时刻workbook只有100行数据在内存,超过就将旧的写到磁盘里减少内存占用
                out.flush();  // 强制清空缓存
                logger.info("after write excel file..." + filename);
            } catch (IOException e) {
                e.printStackTrace();
                logger.error("workbook write out error: ", e);
                log.error(e.getMessage(), e);
            } finally {
                if (out != null) {
                    try {
                        out.close(); // 流关闭
                    } catch (IOException e) {
                        logger.error("out stream close error:", e);
                    }
                }
                workbook.dispose();// 释放workbook所占用的所有windows资源
            }
//            try {
//                renderAttachment(filename, "application/octet-stream", FileUtils.readFileToByteArray(genFile));
//            } catch (IOException e) {
//                log.error(e);
//            }
            logger.info("after return to browser...");
            super.getSession().removeAttribute("exportedFlag");
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage(), e);
        }
    }

注:里面部分涉及到项目相关的处理逻辑,注意辨别。
① 开始时设置的导出前session中exportedFlag任意值,导出后清空,是为了web端获取导出是否成功信息,与poi本身导出逻辑无关;
② 文件名、路径自定义;excel列命名、导出字段自定义;
③ 考虑到数据量大sql查询会比较耗时,故采用了for循环分批查询,每次查询1W数据,这里也与poi导出逻辑无关;
④ getService.query(form),指向的方法,同样是项目自己定义的方法及实现逻辑,这里就不贴出,无非就是根据form、sql去调用底层的jdbctemplate.query()方法查询返回结果集。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/157220.html

(0)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!