使用Java导出数据到Excel

一、excel数据导出限制

1. Excel 2003及以下的版本。一张表最大支持**65536**行数据,256列。也就是说excel2003完全不可能满足百万数据导出的需求。
2. Excel 2007-2010版本。一张表最大支持**1048576**行,16384列;
3. 在针对超过1048576的数据量的数据导出的时候,就不能简单的导出到Excel的单个sheet页中了,必须得采用程序拆分的方式将数据存放到不同的sheet中,如下图所示:

在对一个sheet表中存储数据超过一定数据量的时候,将剩下的数据存放在一个新的sheet页中。

 二、HSSFworkbook,XSSFworkbook,SXSSFworkbook区别

HSSFworkbook

是操作Excel2003以前(包括2003)的版本,扩展名是.xls;

poi导出excel最常用的方式;但是此种方式的局限就是导出的行数至多为65535行,超出65536条后系统就会报错。此方式因为行数不足七万行所以一般不会发生内存不足的情况(OOM)

XSSFWorkbook

是操作Excel2007后的版本,扩展名是.xlsx;

这种形式的出现是为了突破HSSFWorkbook的65535行局限。其对应的是excel2007(1048576行,16384列)扩展名为“.xlsx”,最多可以导出104万行,不过这样就伴随着一个问题---OOM内存溢出,原因是你所创建的book sheet row cell等此时是存在内存的并没有持久化。

SXSSFWorkbook

是操作Excel2007后的版本,扩展名是.xlsx;

从POI 3.8版本开始,提供了一种基于XSSF的低内存占用的SXSSF方式。对于大型excel文件的创建,一个关键问题就是,要确保不会内存溢出。其实,就算生成很小的excel(比如几Mb),它用掉的内存是远大于excel文件实际的size的。如果单元格还有各种格式(比如,加粗,背景标红之类的),那它占用的内存就更多了。对于大型excel的创建且不会内存溢出的,就只有SXSSFWorkbook了。它的原理很简单,用硬盘空间换内存(就像hash map用空间换时间一样)。

SXSSFWorkbook是streaming版本的XSSFWorkbook,它只会保存最新的excel rows在内存里供查看,在此之前的excel rows都会被写入到硬盘里(Windows电脑的话,是写入到C盘根目录下的temp文件夹)。被写入到硬盘里的rows是不可见的/不可访问的。只有还保存在内存里的才可以被访问到。

 SXSSF与XSSF的对比:

1. 在一个时间点上,只可以访问一定数量的数据
2. 不再支持Sheet.clone()
3. 不再支持公式的求值
4. 在使用Excel模板下载数据时将不能动态改变表头,因为这种方式已经提前把excel写到硬盘的了就不能再改了
5. 针对于大数据量的情况下使用SXSSF导出比XSSF导出效率要高得多。

三、SXSSFWorkbook导出excel示例

1. 引入依赖

 <!--poi-ooxml-->
   <dependency>
       <groupId>org.apache.poi</groupId>
       <artifactId>poi-ooxml</artifactId>
       <version>3.9</version>
   </dependency>

2. 编写SxssfWorkbookUtil 

 package com.yuanian.common.utils;
   
   import org.apache.poi.ss.usermodel.*;
   import org.apache.poi.ss.util.CellRangeAddress;
   import org.apache.poi.xssf.streaming.SXSSFWorkbook;
   import org.apache.poi.xssf.usermodel.*;
   
   import java.util.List;
   import java.util.Map;
   
   /**
    * @author zhahsh
    * @date 2021/1/20
    */
   public class SxssfWorkbookUtil {
       public static XSSFCellStyle headCellStyle;
   
       /**
        * 设置单元格样式1
        * @param sxssfWorkbook 工作簿
        * @return 单元格样式
        */
       public static XSSFCellStyle getAndSetXSSFCellStyleOne(SXSSFWorkbook sxssfWorkbook) {
           XSSFCellStyle xssfCellStyle = (XSSFCellStyle) sxssfWorkbook.createCellStyle();
           XSSFDataFormat format = (XSSFDataFormat) sxssfWorkbook.createDataFormat();
           // 是否自动换行
           xssfCellStyle.setWrapText(false);
           // 水平居中
           xssfCellStyle.setAlignment(HorizontalAlignment.CENTER);
           // 垂直居中
           xssfCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
           // 前景颜色
           xssfCellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
           xssfCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());
           // 边框
           xssfCellStyle.setBorderBottom(BorderStyle.THIN);
           xssfCellStyle.setBorderRight(BorderStyle.THIN);
           xssfCellStyle.setBorderTop(BorderStyle.THIN);
           xssfCellStyle.setBorderLeft(BorderStyle.THIN);
           xssfCellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
           xssfCellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
           xssfCellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
           xssfCellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
           // 防止数字过长,excel导出后,显示为科学计数法,如:防止8615192053888被显示为8.61519E+12
           xssfCellStyle.setDataFormat(format.getFormat("0"));
           return xssfCellStyle;
       }
   
   
       /**
        * 设置单元格样式2
        * @param sxssfWorkbook 工作簿
        * @return 单元格样式
        */
       public static XSSFCellStyle getAndSetXSSFCellStyleTwo(SXSSFWorkbook sxssfWorkbook) {
           XSSFCellStyle xssfCellStyle = (XSSFCellStyle) sxssfWorkbook.createCellStyle();
           XSSFDataFormat format = (XSSFDataFormat) sxssfWorkbook.createDataFormat();
           // 是否自动换行
           xssfCellStyle.setWrapText(false);
           // 水平居中
           xssfCellStyle.setAlignment(HorizontalAlignment.CENTER);
           // 边框
           xssfCellStyle.setBorderBottom(BorderStyle.THIN);
           xssfCellStyle.setBorderRight(BorderStyle.THIN);
           xssfCellStyle.setBorderTop(BorderStyle.THIN);
           xssfCellStyle.setBorderLeft(BorderStyle.THIN);
           xssfCellStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex());
           xssfCellStyle.setRightBorderColor(IndexedColors.BLACK.getIndex());
           xssfCellStyle.setTopBorderColor(IndexedColors.BLACK.getIndex());
           xssfCellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());
           // 垂直居中
           xssfCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
           // 防止数字过长,excel导出后,显示为科学计数法,如:防止8615192053888被显示为8.61519E+12
           xssfCellStyle.setDataFormat(format.getFormat("0"));
           return xssfCellStyle;
       }
   
       /**
        * 获取并设置header样式
        */
       public static XSSFCellStyle getAndSetXSSFCellStyleHeader(SXSSFWorkbook sxssfWorkbook) {
           XSSFCellStyle headCellStyle = (XSSFCellStyle) sxssfWorkbook.createCellStyle();
           Font font = sxssfWorkbook.createFont();
           // 字体大小
           font.setFontHeightInPoints((short) 14);
           // 字体粗细
           font.setBold(true);
           // 将字体应用到样式上面
           headCellStyle.setFont(font);
           return headCellStyle;
       }
   
       /**
        * 根据sheet页和head内容填充sheet页头部
        * @param sheet sheet页
        * @param map 题头
        */
       public static final void createTableHeader(Sheet sheet, Map<Integer, List<ExcelBean>> map) {
           int startIndex = 0;//cell起始位置
           int endIndex = 0;//cell终止位置
           for (Map.Entry<Integer, List<ExcelBean>> entry : map.entrySet()) {
               Row row = sheet.createRow(entry.getKey());
               List<ExcelBean> excels = entry.getValue();
               for (int x = 0; x < excels.size(); x++) {
                   //合并单元格
                   if (excels.get(x).getCols() > 1) {
                       if (x == 0) {
                           endIndex += excels.get(x).getCols() - 1;
                           CellRangeAddress range = new CellRangeAddress(0, 0, startIndex, endIndex);
                           sheet.addMergedRegion(range);
                           startIndex += excels.get(x).getCols();
                       } else {
                           endIndex += excels.get(x).getCols();
                           CellRangeAddress range = new CellRangeAddress(0, 0, startIndex, endIndex);
                           sheet.addMergedRegion(range);
                           startIndex += excels.get(x).getCols();
                       }
                       Cell cell = row.createCell(x);
                       cell.setCellValue(excels.get(x).getHeadTextName());// 设置内容
                       if (excels.get(x).getCellStyle() != null) {
                           cell.getCellStyle().cloneStyleFrom(excels.get(x).getCellStyle());// 设置格式
                       }
                       cell.getCellStyle().cloneStyleFrom(headCellStyle);
                   } else {
                       Cell cell = row.createCell(x);
                       cell.setCellValue(excels.get(x).getHeadTextName());// 设置内容
                       cell.setCellStyle(headCellStyle);
                   }
               }
           }
       }
   
   }

3. 编写程序代码

 

   while (rs.next()){
   
                   //当数据量超过1000000行的时候,重新生成一张sheet页,将剩余的数据网新的sheet页中写
                   if (rowIndex >= 1000000){
                       temp++;
                       Sheet sheet1 = workbook.createSheet("转换数据".concat(String.valueOf(temp)));
                       SxssfWorkbookUtil.createTableHeader(sheet1, map);
                       sheets.add(sheet1);
                       rowIndex = 1;
                   }
   
                   Row row = sheets.get(temp).createRow(rowIndex);
                   int width = 300;
                   for(int j=0;j<count;j++){
                       Cell cell = row.createCell(j);
                       String value = "";
                       if (ObjectUtil.isNotNull(rs.getObject(j+1))){
                           value = String.valueOf(rs.getObject(j+1));
                       }
                       if (j == count - 1){
                           switch (value) {
                               //0=未转换;1=转换成功;2=存在未转换数据
                               case "0":
                                   value = "未转换";
                                   break;
                               case "1":
                                   value = "转换成功";
                                   break;
                               case "2":
                                   value = "存在未转换数据";
                           }
                       }
   
                       cell.setCellType(STRING);
                       cell.setCellValue(value);
                       // 获得最大列宽
                       if (!StringUtils.isNULL(value)) {
                           width = value.getBytes().length * 300;
                           cell.setCellStyle(fontStyle2);
                       }else {
   
                           cell.setCellStyle(fontStyle1);
                       }
   
                       // 还未设置,设置当前
                       if (widths.size() <= j) {
                           widths.add(width);
                           continue;
                       }
                       // 比原来大,更新数据
                       if (width > widths.get(j)) {
                           widths.set(j, width);
                       }
                   }
                   rowIndex++;
               }

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值