因项目业务,需要导出百万级数据到excel,在研究了各种方案后,最终确定了用POI的SXSSFWorkbook。

SXSSFWorkbook是POI3.8以上新增的,excel2007后每个sheet支持104万行数据,基于此条件,将得到数据进行分页创建;

并且代码还要通用,无论你传递过来什么对象,多少列都要正常显示。

具体将excel分成4大区域:

  1. 标题(title)
  2. 查询条件(condition):具体封装为map
  3. 列头 (headList):以bean队列形式传递,name为显示的中文名称,column为实际属性
  4. 数据(dataList):以map队列形式传递,key为实际属性(和head中的column要对应),value为实际值(特殊值建议提前格式化)

具体代码如下,只要注意headList中的column和dataList中的column对应,相信能适合大多数场景:

  • 工具类

    ```

package com.cmos.utils;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.OutputStream;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import java.util.TreeMap;

import org.apache.poi.hssf.util.HSSFColor;

import org.apache.poi.ss.usermodel.BorderStyle;

import org.apache.poi.ss.usermodel.Cell;

import org.apache.poi.ss.usermodel.CellStyle;

import org.apache.poi.ss.usermodel.FillPatternType;

import org.apache.poi.ss.usermodel.Font;

import org.apache.poi.ss.usermodel.HorizontalAlignment;

import org.apache.poi.ss.usermodel.Row;

import org.apache.poi.ss.usermodel.Sheet;

import org.apache.poi.ss.usermodel.VerticalAlignment;

import org.apache.poi.ss.util.CellRangeAddress;

import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import org.apache.poi.xssf.usermodel.XSSFRichTextString;

import com.cmos.utils.ExcelData.Head;

/**

  • 导出excel,支持海量数据,可分sheet
  • 实测:50万数据 40s左右
  • 100万数据 80秒左右
  • (具体和运行环境有关)
  • @author zsx
  • */

    public class ExcelUtil {

    final int memorySize = 100;//内存中只创建100个对象,写临时文件,当超过100条,就将内存中不用的对象释放

    final int sheetRowMax = 200000;// 单sheet最大行数,用于分页

    /**

    • 动态创建excel,将所有数据以ExcelData方式传递进来:
    • title:标题,用于显示在最上方
    • condition:查询条件,以map形式传递
    • headList:列头,以bean队列形式传递,name为显示的中文名称,column为实际属性
    • dataList:数据,以map队列形式传递,key为实际属性(和head中的column要对应),value为实际值(特殊值建议提前格式化)

      */

      public void export(ExcelData data,File file) throws IOException{

      long st = System.currentTimeMillis();

      ExcelUtil.judeFileExists(file);

      ExcelUtil eu = new ExcelUtil();

      OutputStream out = new FileOutputStream(file.getPath());

      eu.exportExcel(data, out);

      out.close();

      long et = System.currentTimeMillis();

      System.out.println("export excel:"+(et-st)+"ms");

      }

    /**

    • 导出核心
    • @param data
    • @param out

      */

      private void exportExcel(ExcelData data, OutputStream out) {

      // 校验

      if (data == null || data.getHeadList() == null || data.getDataList() == null) {

      System.out.println("准备数据不能为空");

      return;

      }

      // 数据准备

      String title = data.getTitle();

      Map

      //为防止数据量过大导致OOM,POI3.8以上支持大数据导出

      SXSSFWorkbook workbook = new SXSSFWorkbook(memorySize);

      Sheet sheet = null;

      int sheetSize = 1;

      if (dataList.size() > sheetRowMax) {

      if (dataList.size() % sheetRowMax == 0) {

      sheetSize = dataList.size() / sheetRowMax;

      } else {

      sheetSize = dataList.size() / sheetRowMax + 1;

      }

      }

      // 设置样式

      CellStyle titleStyle = getTitleStyle(workbook);

      CellStyle HeadStyle = getHeadStyle(workbook);

      CellStyle dataStyle = getDataStyle(workbook);

      // 设置数据

      for (int i = 0; i < sheetSize; i++) {

      int startInx = sheetRowMaxi;

      int endInx = sheetRowMax
      (i+1)-1;

      //获取当页数据

      if(sheetRowMax*(i+1)-1 > dataList.size()){

      endInx = dataList.size();

      }

      List

      }

      try {

      workbook.write(out);

      } catch (IOException e) {

      e.printStackTrace();

      }

    }

    /**

    • 合并单元格
    • @param sheet
    • @param headSize
    • @param conditionSize

      */

      private void mergeCell(Sheet sheet, int headSize, int conditionSize) {

      //TODO 合并后样式被改变,需要进行优化 by zsx 20170717

      CellRangeAddress titleCra = new CellRangeAddress(0, 0, 0, headSize - 1);

      sheet.addMergedRegion(titleCra);

      for (int i = 0; i < conditionSize; i++) {

      CellRangeAddress conditionKeyCra = new CellRangeAddress(i + 1, i + 1, 0, 1);

      sheet.addMergedRegion(conditionKeyCra);

      CellRangeAddress conditionValueCra = new CellRangeAddress(i + 1, i + 1, 2, 3);

      sheet.addMergedRegion(conditionValueCra);

      }

    }

    /**

    • 创建数据
    • @param sheet
    • @param cellStyle
    • @param i
    • @param dataList

      */

      private void createDataRow(Sheet sheet, CellStyle cellStyle, int index,List headList, List

    /**

    • 创建列头
    • @param sheet
    • @param cellStyle
    • @param j
    • @param headList

      */

      private void createHeadRow(Sheet sheet, CellStyle cellStyle, int index, List headList) {

      Row row = sheet.createRow(index);

      row.setHeight((short) 300);

      for(int i=0;i<headList.size();i++){

      Cell cell = row.createCell(i);

      cell.setCellStyle(cellStyle);

      XSSFRichTextString text = new XSSFRichTextString(headList.get(i).getName());

      cell.setCellValue(text);

      }

      }

    /**

    • 创建查询
    • @param sheet
    • @param cellStyle
    • @param condition

      */

      private void createConditionRow(Sheet sheet, CellStyle cellStyle, Map

    }

    /**

    • 设置列宽
    • @param sheet
    • @param headList

      */

      private void setSheetWidth(Sheet sheet, List headList) {

      for (int i = 0; i < headList.size(); i++) {

      sheet.setColumnWidth(i, 4000);

      }

      }

    /**

    • 创建标题
    • @param sheet
    • @param cellStyle
    • @param title

      */

      private void createTitleRow(Sheet sheet, CellStyle cellStyle, String title) {

      Row row = sheet.createRow(0);

      row.setHeight((short) 500);

      Cell cell = row.createCell(0);

      cell.setCellStyle(cellStyle);

      XSSFRichTextString text = new XSSFRichTextString(title);

      cell.setCellValue(text);

      }

    /**

    • 设置标题样式
    • @param workbook
    • @return

      */

      private CellStyle getTitleStyle(SXSSFWorkbook workbook) {

      // 生成一个样式(标题行)

      CellStyle headStyle = workbook.createCellStyle();

      // 设置这些样式

      headStyle.setFillForegroundColor(HSSFColor.WHITE.index);

      headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

      headStyle.setBorderBottom(BorderStyle.THIN);

      headStyle.setBorderLeft(BorderStyle.THIN);

      headStyle.setBorderRight(BorderStyle.THIN);

      headStyle.setBorderTop(BorderStyle.THIN);

      headStyle.setAlignment(HorizontalAlignment.CENTER);

      headStyle.setVerticalAlignment(VerticalAlignment.CENTER);

      // 生成一个字体

      Font headFont = workbook.createFont();

      headFont.setFontHeightInPoints((short) 16);

      headFont.setFontName("Microsoft YaHei UI Light");

      headFont.setBold(true);

      headFont.setBold(true);

      // 把字体应用到当前的样式

      headStyle.setFont(headFont);

      return headStyle;

      }

    /**

    • 设置列头样式
    • @param workbook
    • @return

      */

      private CellStyle getHeadStyle(SXSSFWorkbook workbook) {

      // 生成一个样式(标题行)

      CellStyle headStyle = workbook.createCellStyle();

      // 设置这些样式

      headStyle.setFillForegroundColor(HSSFColor.GREY_25_PERCENT.index);

      headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

      headStyle.setBorderBottom(BorderStyle.THIN);

      headStyle.setBorderLeft(BorderStyle.THIN);

      headStyle.setBorderRight(BorderStyle.THIN);

      headStyle.setBorderTop(BorderStyle.THIN);

      headStyle.setAlignment(HorizontalAlignment.CENTER);

      headStyle.setAlignment(HorizontalAlignment.CENTER);

      // 生成一个字

      Font headFont = workbook.createFont();

      headFont.setFontHeightInPoints((short) 12);

      headFont.setFontName("Microsoft YaHei UI Light");

      headFont.setBold(true);

      // 把字体应用到当前的样式

      headStyle.setFont(headFont);

      return headStyle;

      }

    /**

    • 设置数据样式
    • @param workbook
    • @return

      */

      private CellStyle getDataStyle(SXSSFWorkbook workbook) {

      // 生成一个样式(标题行)

      CellStyle headStyle = workbook.createCellStyle();

      // 设置这些样式

      headStyle.setFillForegroundColor(HSSFColor.WHITE.index);

      headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

      headStyle.setBorderBottom(BorderStyle.THIN);

      headStyle.setBorderLeft(BorderStyle.THIN);

      headStyle.setBorderRight(BorderStyle.THIN);

      headStyle.setBorderTop(BorderStyle.THIN);

      headStyle.setAlignment(HorizontalAlignment.CENTER);

      // 生成一个字体

      Font headFont = workbook.createFont();

      headFont.setFontHeightInPoints((short) 12);

      headFont.setFontName("Microsoft YaHei UI Light");

      // 把字体应用到当前的样式

      headStyle.setFont(headFont);

      return headStyle;

      }

    /**

    • 判断文件是否存在
    • @param file

      */

      public static void judeFileExists(File file) {

      if (!file.exists()) {

      try {

      file.createNewFile();

      } catch (IOException e) {

      e.printStackTrace();

      }

      }

      }

    /**

    • 使用 Map按key进行排序
    • @param map
    • @return

      */

      public static Map

}

- 测试用例

package test.cmos;

import java.io.File;

import java.io.IOException;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import org.junit.Test;

import com.cmos.utils.ExcelData;

import com.cmos.utils.ExcelData.Head;

import com.cmos.utils.ExcelUtil;

public class ExcelTest{

//模拟数据量
static int dataSize = 500000;

@Test
public void testExport() throws IOException {
    ExcelData data = createData();
    new ExcelUtil().export(data, new File("E:/data.xlsx"));
}

/**
 * 模拟生成数据
 * @return
 */
private static ExcelData createData() {
    long a = System.currentTimeMillis();
    ExcelData data = new ExcelData();
    data.setTitle("商户对账差异明细");
    Map<String, String> c = new HashMap<String, String>();
    c.put("来源系统", "10085销售订单");
    c.put("对账日期", "2016-11-03到2016-11-05");
    c.put("商户", "华为");
    data.setCondition(c);
    List<Head> headList = new ArrayList<Head>();
    headList.add(new Head("来源系统", "a"));
    headList.add(new Head("批次号", "b"));
    headList.add(new Head("商户编号", "c"));
    headList.add(new Head("商户名称", "d"));
    headList.add(new Head("业务日期", "e"));
    headList.add(new Head("订单号", "f"));
    headList.add(new Head("我方金额", "g"));
    headList.add(new Head("对方金额", "h"));
    headList.add(new Head("差额", "i"));
    data.setHeadList(headList);

    List<Map<String, Object>> dataList = new ArrayList<Map<String, Object>>();
    for (int i = 0; i < dataSize; i++) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("a", "a" + i);
        map.put("b", "b" + i);
        map.put("c", "c" + i);
        map.put("d", "d" + i);
        map.put("e", "e" + i);
        map.put("f", "f" + i);
        map.put("g", "g" + i);
        map.put("h", "h" + i);
        map.put("i", "i" + i);
        dataList.add(map);
    }
    data.setDataList(dataList);
    long b = System.currentTimeMillis();
    System.out.println("create data:"+(b-a)+"ms");
    return data;
}

}

- 传入的导出对象

package com.cmos.utils;

import java.util.List;

import java.util.Map;

public class ExcelData {

//标题

private String title;

//查询条件

private Map

public String getTitle() {
    return title;
}
public void setTitle(String title) {
    this.title = title;
}

public List<Head> getHeadList() {
    return headList;
}
public void setHeadList(List<Head> headList) {
    this.headList = headList;
}
public List<Map<String, Object>> getDataList() {
    return dataList;
}
public void setDataList(List<Map<String, Object>> dataList) {
    this.dataList = dataList;
}
public Map<String, String> getCondition() {
    return condition;
}
public void setCondition(Map<String, String> condition) {
    this.condition = condition;
}

public static class Head{
    private String name;
    private String column;
    public Head(){
    }
    public Head(String name,String column){
        this.name = name;
        this.column = column;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getColumn() {
        return column;
    }
    public void setColumn(String column) {
        this.column = column;
    }

}

}

```

Excel导出百万级数据解决方案的更多相关文章

  1. 使用POI导出百万级数据到excel的解决方案

    1.HSSFWorkbook 和SXSSFWorkbook区别 HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls,一张表最大支持65536行数据,256列 ...

  2. poi实现百万级数据导出

    注意使用 SXSSFWorkbook 此类在构造表格和处理行高的时候效率极高,刚开始时我使用的 XSSFWorkbook 就出现构造表格效率极低,一万行基本需要3秒左右,那当导出百万级数据就慢的要死啦 ...

  3. java 分页导出百万级数据到excel

    最近修改了一个导出员工培训课程的历史记录(一年数据),导出功能本来就有的,不过前台做了时间限制(只能选择一个月时间内的),还有一些必选条件, 导出的数据非常有局限性.心想:为什么要做出这么多条件限制呢 ...

  4. 记一次针对excel导出的优化

    最近发现我们系统导出excel文件时由于是导出百万级数据导出,速度过慢并且内存占用多,故进行了下面的一次优化. 我们使用apache的poi进行excel文件操作 主要耗时: 1.从数据库得到需要导出 ...

  5. JAVA笔记-如何将百万级数据高效的导出到Excel表单

    今天,一朋友问我使用JAVA有没有什么办法导出百万级的数据到Excel工作表. 当时我的第一个念头就是这真的是一个好疯狂的念头.然后就想假如真的有这样类似的需求,我自己应该怎么做呢? ps: 首先科普 ...

  6. Atitit.excel导出 功能解决方案 php java C#.net版总集合.doc

    Atitit.excel导出 功能解决方案 php java C#.net版总集合.docx 1.1. Excel的保存格式office2003 office2007/2010格式1 1.2. 类库选 ...

  7. 问问题_Java一次导出百万条数据生成excel(web操作)

    需求:在web页面操作,一次导出百万条数据并生成excel 分析: 1.异步生成Excel,非实时,完成后使用某种方式通知用户 2.生成多个excel文件,并打包成zip文件,因为一个excel容纳不 ...

  8. Atitit.导出excel功能的设计 与解决方案

    Atitit.导出excel功能的设计 与解决方案 1.1. 项目起源于背景1 1.2. Js  jquery方案(推荐)jquery.table2excel1 1.3. 服务器方案2 1.4. 详细 ...

  9. Excel导入数据库百万级数据瞬间插入

    Excel导入数据库百万级数据瞬间插入 百万级别,瞬间,有点吊哇

随机推荐

  1. 【JAVAWEB学习笔记】网上商城实战:环境搭建和完成用户模块

    网上商城实战 今日任务 完成用户模块的功能 1.1      网上商城的实战: 1.1.1    演示网上商城的功能: 1.1.2    制作目的: 灵活运用所学知识完成商城实战. 1.1.3    ...

  2. SonarQube Scanner的配置与使用简介

    一.下载 下载地址: https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-3.0.3.77 ...

  3. UWP自定义RadioButton实现Tab底部导航

    先看效果: 参照Android的实现方式用RadioButton来实现,但是Uwp的RadioButton并没有安卓的Selector选择器 下面是一个比较简单的实现,如果有同学有更好的实现,欢迎留言 ...

  4. 字符串的拼接python

    数字可以强制转换为字符串,但是字符串不能强制转换为数字(会报错) a='abcs' b='dsys' 方法一.a+b 最low的一个方法,因为每+一次内存增加一次 方法二.print '%s%s'%( ...

  5. Python的核心数据结构

    数据结构 例子 数字 1234,3.1415,3+4j 字符串 'spam'."grace's" 列表 [1,[2,'three'],4] 字典 {'food':'spam','t ...

  6. node.js零基础详细教程(5):express 、 路由

    第五章 建议学习时间4小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...

  7. php简单的文件操作

    (1)先要想好要操作哪个文件? (2)确定文件的路径? (3)要有什么文件管理功能? 一.先做一下简单的查看文件功能,文件中的文件和文件夹都显示,但是双击文件夹可以显示下一级子目录,双击"返 ...

  8. 14.Java中的Future模式

    jdk1.7.0_79  本文实际上是对上文<13.ThreadPoolExecutor线程池之submit方法>的一个延续或者一个补充.在上文中提到的submit方法里出现了Future ...

  9. iframe访问子页面方法

    在Iframe中调用子页面的Js函数 调用IFRAME子页面的JS函数 说明:假设有2个页面,index.html和inner.html.其中index.html中有一个iframe,这个iframe ...

  10. 数据结构与算法(c++)——查找二叉树与中序遍历

    查找树ADT--查找二叉树 定义:对于树中的每个节点X,它的左子树中的所有项的值小于X中的项,而它的右子树中所有项的值大于X中的项. 现在给出字段和方法定义(BinarySearchTree.h) # ...