POI报表高级操作

上篇文章已经介绍Excel可以分为Excel2003和Excel2007两种版本,Excel2003在POI中使用HSSF对象,一个sheet最多允许65536条数据,处理较少数据时可以使用,但是处理百万数据时Excel2003肯定容纳不了;Excel2007在POI中使用XSSF对象,最多允许一个sheet存储1048576条数据,表示其已经可以支持百万数据,但是在实际运行可能中还存在问题,原因是POI报表所产生的对象,单元格对象,字体对象,都不会销毁,导致了可能存在OutOfMemoryError(OOM)内存溢出风险。

百万数据报表导出概述

对于百万数据Excel的导出,基本只限于讨论Excel2007版本的使用方法。ApachePOI提供三种方式解决大数据量的数据导入导出:

  • 用户模式:用户模式有许多封装好的方法,易于操作,但创建对象太多,内存消耗巨大
  • 事件模式:基于SAX(Simple API for XML)方式解析XML,是一个接口,也是一个软件包,XML解析的一种替代方式,和DOM解析不同点在于逐行解析,不一次性将数据全部加载到内存,相当于一边扫面一边解析。
  • SXSSF对象:是用于生成海量Excel的文件,主要借助临时存储空间生成Excel

    百万数据的导出

需求分析

在互联网时代,百万数据经常产生,有多种多样的原因需要数据导出

解决方式

  • 1.思路分析:

使用POI的XSSFWORK对象进行导出Excel报表,是将所有的数据一次性到单元格对象,并保存到内存中,当单元格全部创建完成才会一次性写入到Excel进行导出。当达到百万级别的数据导出时,随着单元格对象的不断创建,内存中的数据越来越多,直到OOM。POI的SXSSFWORK对象,是专门用于处理大数据量的Excel导出的。

2.原理分析:

在实例化SXSSFWork对象时,可以指定内存中所产生的POI对象数量(默认100),一旦内存对象个数达到指定数量,就将内存中的数据写入到磁盘,就可以将内存中数据进行销毁,以此循环直到Excel导出完成

代码实现

还是以用户数据为例子

在FileUtil中添加

对之前的做了一些小改动

package com.cn.greemes.common.util;import cn.hutool.core.collection.CollUtil;import cn.hutool.core.io.IoUtil;import cn.hutool.core.util.IdUtil;import cn.hutool.poi.excel.BigExcelWriter;import cn.hutool.poi.excel.ExcelUtil;import org.apache.poi.ss.usermodel.Cell;import org.apache.poi.ss.usermodel.Row;import org.apache.poi.ss.usermodel.Sheet;import org.apache.poi.ss.usermodel.Workbook;import org.apache.poi.xssf.streaming.SXSSFSheet;import org.apache.poi.xssf.streaming.SXSSFWorkbook;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.File;import java.io.IOException;import java.net.URLEncoder;import java.util.Iterator;import java.util.List;import java.util.Map;import java.util.concurrent.atomic.AtomicInteger;/** * 文件操作 */public class FileUtil {    public static final String SYS_TEM_DIR =System.getProperty("java.io.tmpdir")+ File.separator;    public  void downloadExcel(List<Map<String, String>> list, HttpServletResponse response) throws IOException {        String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";        File file = new File(tempPath);        BigExcelWriter writer = ExcelUtil.getBigWriter(file);        // 一次性写出内容,使用默认样式,强制输出标题        writer.write(list, true);        SXSSFSheet sheet = (SXSSFSheet)writer.getSheet();        //上面需要强转SXSSFSheet  不然没有trackAllColumnsForAutoSizing方法        sheet.trackAllColumnsForAutoSizing();        //列宽自适应       // writer.autoSizeColumnAll();        //response为HttpServletResponse对象        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");        //test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码        response.setHeader("Content-Disposition", "attachment;filename=file.xlsx");        ServletOutputStream out = response.getOutputStream();        // 终止后删除临时文件        file.deleteOnExit();        writer.flush(out, true);        //此处记得关闭输出Servlet流        IoUtil.close(out);    }    public  void downloadExcelBySXSSF(List<Map<String, String>> list, HttpServletResponse response) throws IOException {        String tempPath = SYS_TEM_DIR + IdUtil.fastSimpleUUID() + ".xlsx";        File file = new File(tempPath);        //2.创建工作簿         SXSSFWorkbook workbook = new SXSSFWorkbook();        //3.构造sheet        Sheet sheet =workbook.createSheet();        //创建表头        Row row = sheet.createRow(0);        Map<String,String> mapfirst = list.get(0);        String listHead = null;        AtomicInteger headersAi = new AtomicInteger();        for (String key : mapfirst.keySet()) {           Cell cell = row.createCell(headersAi.getAndIncrement());           cell.setCellValue(key);        }        AtomicInteger datasAi = new AtomicInteger(1);        Cell cell =null;        for(Map<String, String> map : list){            Row dataRow = sheet.createRow(datasAi.getAndIncrement());            int i=0;            for (String key : map.keySet()) {                Cell cell1 = dataRow.createCell(datasAi.getAndIncrement());                String value= (String)map.get(key);                cell = dataRow.createCell(i);                cell1.setCellValue(value);                i++;            }        }        String fileName = URLEncoder.encode("用户信息.xlsx", "UTF-8");        response.setContentType("application/octet-stream");        response.setHeader("content-disposition", "attachment;filename=" + new                String(fileName.getBytes("ISO8859-1")));        response.setHeader("filename", fileName);        workbook.write(response.getOutputStream());    }}

在controller进行配置

 @ApiOperation("导出用户数据")    @RequestMapping(value = "/export2", method = RequestMethod.GET)    @ResponseBody    public void export2(HttpServletResponse response, @RequestParam(value = "keyword", required = false) String keyword,                       @RequestParam(value = "pageSize", defaultValue = "5") Integer pageSize,                       @RequestParam(value = "pageNum", defaultValue = "1") Integer pageNum) throws UnsupportedEncodingException, IOException {        Page<MesAdmin> adminList = adminService.list(keyword, pageSize, pageNum);        List<Map<String,String>> list = new ArrayList();        //因为只有七条数据,所以做了多次循环添加数据        for(int i=0;i<149795;i++) {            for (MesAdmin umsAdmin : adminList.getRecords()) {                Map<String, String> map = new HashMap<>(6);                DateFormat d1 = DateFormat.getDateInstance();                map.put("姓名", umsAdmin.getUsername());                map.put("邮箱", umsAdmin.getEmail());                map.put("昵称", umsAdmin.getNickName());                map.put("备注信息", umsAdmin.getNote());                map.put("创建时间", d1.format( umsAdmin.getCreateTime()));                String loginTime ="";                if(umsAdmin.getLoginTime()!=null){                    loginTime=d1.format( umsAdmin.getLoginTime());                }                map.put("最后登录时间",loginTime );                list.add(map);            }        }        fileUtil.downloadExcelBySXSSF(list,response);    }

结束语:

本来要介绍数据的导出,可是发现百万级别的数据导出也需要介绍一下,明天介绍数据的导出

Github地址:

github地址:https://github.com/bangbangzhou/greemes/tree/master

公众号

SpringBoot结合POI百万级数据报表操作

更多相关文章

  1. 数据结构--时间复杂度与空间复杂度
  2. 什么是 Java 对象分配率
  3. Java 为什么需要保留基本数据类型
  4. 如何用 Map 对象创建 Set 对象
  5. 数据库-关系代数
  6. 数据库-范式
  7. 数据库数据完整性
  8. 数据库-事务处理
  9. 用map代替纯JavaScript对象[每日前端夜话0xCF]

随机推荐

  1. 学习笔记: shell 中的 set -e , set +e 用
  2. linux 服务器间文件传输
  3. Zabbix - LINUX下CPU,硬盘,流量,内存监控
  4. 读书笔记-Linux C 编程从基础到实践-第一
  5. 用GCC编译链接程序--编译链接器GCC常用功
  6. 嵌入式Linux文件系统及其存储机制分析
  7. Linux系列:linux学习之路(入门类、编程类、
  8. Oracle表按字段和|分格符导出文件
  9. 《乐者为王——自由软件Linux之父李纳斯&
  10. Shell脚本创建linux用户帐户但密码出错