Excel导出百万级数据解决方案
因项目业务,需要导出百万级数据到excel,在研究了各种方案后,最终确定了用POI的SXSSFWorkbook。
SXSSFWorkbook是POI3.8以上新增的,excel2007后每个sheet支持104万行数据,基于此条件,将得到数据进行分页创建;
并且代码还要通用,无论你传递过来什么对象,多少列都要正常显示。
具体将excel分成4大区域:
- 标题(title)
- 查询条件(condition):具体封装为map
- 列头 (headList):以bean队列形式传递,name为显示的中文名称,column为实际属性
- 数据(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导出百万级数据解决方案的更多相关文章
- 使用POI导出百万级数据到excel的解决方案
1.HSSFWorkbook 和SXSSFWorkbook区别 HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls,一张表最大支持65536行数据,256列 ...
- poi实现百万级数据导出
注意使用 SXSSFWorkbook 此类在构造表格和处理行高的时候效率极高,刚开始时我使用的 XSSFWorkbook 就出现构造表格效率极低,一万行基本需要3秒左右,那当导出百万级数据就慢的要死啦 ...
- java 分页导出百万级数据到excel
最近修改了一个导出员工培训课程的历史记录(一年数据),导出功能本来就有的,不过前台做了时间限制(只能选择一个月时间内的),还有一些必选条件, 导出的数据非常有局限性.心想:为什么要做出这么多条件限制呢 ...
- 记一次针对excel导出的优化
最近发现我们系统导出excel文件时由于是导出百万级数据导出,速度过慢并且内存占用多,故进行了下面的一次优化. 我们使用apache的poi进行excel文件操作 主要耗时: 1.从数据库得到需要导出 ...
- JAVA笔记-如何将百万级数据高效的导出到Excel表单
今天,一朋友问我使用JAVA有没有什么办法导出百万级的数据到Excel工作表. 当时我的第一个念头就是这真的是一个好疯狂的念头.然后就想假如真的有这样类似的需求,我自己应该怎么做呢? ps: 首先科普 ...
- Atitit.excel导出 功能解决方案 php java C#.net版总集合.doc
Atitit.excel导出 功能解决方案 php java C#.net版总集合.docx 1.1. Excel的保存格式office2003 office2007/2010格式1 1.2. 类库选 ...
- 问问题_Java一次导出百万条数据生成excel(web操作)
需求:在web页面操作,一次导出百万条数据并生成excel 分析: 1.异步生成Excel,非实时,完成后使用某种方式通知用户 2.生成多个excel文件,并打包成zip文件,因为一个excel容纳不 ...
- Atitit.导出excel功能的设计 与解决方案
Atitit.导出excel功能的设计 与解决方案 1.1. 项目起源于背景1 1.2. Js jquery方案(推荐)jquery.table2excel1 1.3. 服务器方案2 1.4. 详细 ...
- Excel导入数据库百万级数据瞬间插入
Excel导入数据库百万级数据瞬间插入 百万级别,瞬间,有点吊哇
随机推荐
- 从零开始理解JAVA事件处理机制(1)
“事件”这个词已经被滥用了.正因为“事件”的被滥用,很多人在用到事件的时候不求甚解,依样画葫芦,导致学习工作了很多年,还是不清楚什么是事件处理器.什么是事件持有者.所以,如果你对于Event这个词还是 ...
- 从零开始理解JAVA事件处理机制(2)
第一节中的示例过于简单<从零开始理解JAVA事件处理机制(1)>,简单到让大家觉得这样的代码简直毫无用处.但是没办法,我们要继续写这毫无用处的代码,然后引出下一阶段真正有益的代码. 一:事 ...
- RMAN备份与恢复(二)--常用操作学习
(1)连接目标数据库 在RMAN中可以建立与目标数据库或恢复目录数据库的连接.与目标数据库连接时,用户须具有sysdba系统权限,以保证可以进行数据库的备份.修复与恢复工作. 可以在操作系统命令提示符 ...
- 【原创】源码角度分析Android的消息机制系列(一)——Android消息机制概述
ι 版权声明:本文为博主原创文章,未经博主允许不得转载. 1.为什么需要Android的消息机制 因为Android系统不允许在子线程中去访问UI,即Android系统不允许在子线程中更新UI. 为什 ...
- sed的用法
1.什么是sed sed命令是一个流线式.非交互式编辑器,可以实现在vi等编辑器中一样的编辑效果. 2.sed的工作原理 模式空间(pattern space) sed一次处理一行文本(或输入), ...
- Nginx——在Windows环境下安装
下载 Nginx是开源软件,用户可以访问 http://nginx.org/ 网站获取源码包或Windows二进制文件下载.其中1.13.x版本为开发版本,1.12.0版本为稳定版本.开发版本分支会较 ...
- phpcms V9 后台验证码图片不显示
某个网站在本地运行成功,上传到服务器上后,发现后台登陆的验证码图片不显示 根据网上提供的解决方案, 网站路径变量web_path没问题 database.system的配置路径没问题 apache的G ...
- java 实例变量的初始化
1.对于实例变量,该类没创建一次实例,就需要为实例变量分配一块内存空间:2.程序通过Person对象来访问eyeNum类变量时,底层依然会转换为通过Person访问eyeNum类变量:3.当Perso ...
- Redis客户端管理工具,状态监控工具
TreeNMS是一款Redis web客户端管理工具,采用JAVA开发,实现基于web方式对Redis数据库进行管理.监控.数据维护. 功能包括:数据库的状态监控,库表的展示,key,value的展示 ...
- CoolBlog开发笔记第3课:创建Django应用
教程目录 1.1 CoolBlog开发笔记第1课:项目分析 1.2 CoolBlog开发笔记第2课:搭建开发环境 前言 经过上一节我们已经创建了CoolBlog工程,但是关于CoolBlog的功能代码 ...