c#后台如何导出excel到本地_POI如何高效导出百万级Excel数据?

93c26ff9cc21b582f7c26a65ac896dde.png

阅读原文:POI如何高效导出百万级Excel数据?


在一个具有统计功能的系统中,导出excel功能几乎是一定的,如何导出excel?导出的数据有多少?如何高效的导出?

Excel简介
什么是excel就不用介绍了,这里主要说明不同版本下每个sheet下的行列限制。

7637381369f237e764277919f2597f8e.png

由上面可知 Excel 2003及以下是无法实现单sheet百万级的数据。

Apache POI

  • 简介

Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office(Excel、WORD、PowerPoint、Visio等)格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“可怜的模糊实现”。

  • 常用类

HSSF - 提供读写Microsoft Excel XLS格式档案的功能。
XSSF - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
SXSSF - 一种基于XSSF的低内存占用的API(3.8版本开始出现)。
HWPF - 提供读写Microsoft Word DOC97格式档案的功能。
XWPF - 提供读写Microsoft Word DOC2003格式档案的功能。
HSLF - 提供读写Microsoft PowerPoint格式档案的功能。
HDGF - 提供读Microsoft Visio格式档案的功能。
HPBF - 提供读Microsoft Publisher格式档案的功能。
HSMF - 提供读Microsoft Outlook格式档案的功能。
我们这里是导出Excel,所以使用的是前三个。

导出策略

  • 方案

使用XSSF和SXSSF分别导入1w,10w,100w数据
使用SXSSF,SXSSF以10w分页,SXSSF多线程以10w分页导入100w数据

  • 性能对比

时间不包含网络耗时

0b442b5c37cd186d8efc51898f0cc9e7.png
  • 总结

方案一:
数据在万条时XSSF和SXSSF相差不大
数据上十万后SXSSF性能开始突出
数据到达百万时,XSSF已不适合使用


方案二:
不进行分表时,SXSSF最多可存储1048576行
百万级数据分表存储时,使用多线程导出几乎是不使用多线程导出的一半时间

最终我得出一个导出百万级数据的最高效方案:多线程分表导出实战

实战

  • controller层
@RestController 
@RequestMapping("export")
public class ReportController {

 public static final String[] TITLE = new String[]{"第1列", "第2列", "第3列", "第4列", "第5列"};
 public static final String SHEET_NAME = "page1";

 @RequestMapping(value = "/sxssf/page/thread")
 @ResponseBody
 public void exportSXSSFWorkbookByPageThread(HttpServletResponse response, Integer num) throws Exception {

 //excel文件名
 String fileName = System.currentTimeMillis() + ".xlsx";

 //sheet名
 if (Objects.isNull(num)) {
            num = 65536;
 }
 String[][] content = buildContent(num);
 long start = System.currentTimeMillis();
 SXSSFWorkbook wb = ExcelUtil.getSXSSFWorkbookByPageThread(TITLE, content, null);
 long millis = System.currentTimeMillis() - start;
 long second = millis / 1000;
 System.out.println("SXSSF Page Thread 导出" + num + "条数据,花费:" + second + "s/ " + millis + "ms");
        writeAndClose(response, fileName, wb);
        wb.dispose();
 }

 /**
     * 构建内容
     * @param num
     * @return
     */
 private String[][] buildContent(Integer num) {
 String[][] content = new String[num][5];
 for (int i = 0; i < content.length; i++) {
            content[i][0] = "1";
            content[i][1] = "2";
            content[i][2] = "3";
            content[i][3] = "4";
            content[i][4] = "5";
 }
 return content;
 }

 private void writeAndClose(HttpServletResponse response, String fileName, Workbook wb) {
 try {
 this.setResponseHeader(response, fileName);
 OutputStream os = response.getOutputStream();
            wb.write(os);
            os.flush();
            os.close();
 } catch (Exception e) {
            e.printStackTrace();
 }
 }

 public void setResponseHeader(HttpServletResponse response, String fileName) {
 try {
 try {
                fileName = new String(fileName.getBytes(), "UTF-8");
 } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
 }
            response.setContentType("application/octet-stream;charset=ISO8859-1");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            response.addHeader("Pargam", "no-cache");
            response.addHeader("Cache-Control", "no-cache");
 } catch (Exception ex) {
            ex.printStackTrace();
 }
 }
}
  • 工具类
public class ExcelUtil {
 public static final int PER_SHEET_LIMIT = 500000;

 public static SXSSFWorkbook getSXSSFWorkbookByPageThread(String[] title, String[][] values) {

 SXSSFWorkbook wb = new SXSSFWorkbook();

 int pageNum = values.length / PER_SHEET_LIMIT;
 int lastCount = values.length % PER_SHEET_LIMIT;

 if (values.length > PER_SHEET_LIMIT) {

 CellStyle style = wb.createCellStyle();
 int sheet = lastCount == 0 ? pageNum : pageNum + 1;
 CountDownLatch downLatch = new CountDownLatch(sheet);
 Executor executor = Executors.newFixedThreadPool(sheet);

 for (int c = 0; c <= pageNum; c++) {
 int rowNum = PER_SHEET_LIMIT;
 if (c == pageNum) {
 if (lastCount == 0) {
 continue;
 }
                  rowNum = lastCount;
 }
 Sheet sheet = wb.createSheet("page" + c);
              executor.execute(new PageTask(downLatch, sheet, title, style, rowNum, values));
 }
 try {
              downLatch.await();
 } catch (InterruptedException e) {
              e.printStackTrace();
 }
 }
 return wb;
 }
}
  • 分表任务类
public class PageTask implements Runnable {
 private CountDownLatch countDownLatch;

 private Sheet sheet;
 private String[] title;
 private CellStyle style;
 private int b;
 private String[][] values;

 public PageTask(CountDownLatch countDownLatch, Sheet sheet, String[] title, CellStyle style, int b, String[][] values) {
 this.countDownLatch = countDownLatch;
 this.sheet = sheet;
 this.title = title;
 this.style = style;
 this.b = b;
 this.values = values;
 }

 @Override
 public void run() {

 try {
 Row row = sheet.createRow(0);

 Cell cell = null;

 for (int i = 0; i < title.length; i++) {
                cell = row.createCell(i);
                cell.setCellValue(title[i]);
                cell.setCellStyle(style);
 }

 for (int i = 0; i < b; i++) {
                row = sheet.createRow(i + 1);
 for (int j = 0; j < values[i].length; j++) {
                    row.createCell(j).setCellValue(values[i][j]);
 }
 }
 } catch (Exception e) {
            e.printStackTrace();
 } finally {
 if (countDownLatch != null) {
                countDownLatch.countDown();
 }
 }
 }
}

更多技术文章尽在微信公众号 : 码上实战

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
具体内容请参考我的BLOG:http://blog.csdn.net/smallwhiteyt/archive/2009/11/08/4784771.aspx 如果你耐心仔细看完本文,相信以后再遇到导出EXCLE操作的时候你会很顺手觉得SO EASY,主要给新手朋友们看的,老鸟可以直接飘过了,花了一晚上的时间写的很辛苦,如果觉得对你有帮助烦请留言支持一下,我会写更多基础的原创内容来回报大家。 C#导出数据EXCEL表格是个老生常谈的问题了,写这篇文章主要是给和我一样的新手朋友提供两种导出EXCEL的方法并探讨一下导出的效率问题,本文中的代码直接就可用,其中部分代码参考其他的代码并做了修改,抛砖引玉,希望大家一起探讨,如有不对的地方还请大家多多包涵并指出来,我也是个新手,出错也是难免的。 首先先总结下自己知道的导出EXCEL表格的方法,大致有以下几种,有疏漏的请大家补充。 1.数据逐条逐条的写入EXCEL 2.通过OLEDB把EXCEL做为数据源来写 3.通过RANGE范围写入多行多列内存数据EXCEL 4.利用系统剪贴板写入EXCEL 好了,我想这些方法已经足够完成我们要实现的功能了,方法不在多,在精,不是么?以上4中方法都可以实现导出EXCEL,方法1为最基础的方法,意思就是效率可能不是太高,当遇到数据量过大时所要付出的时间也是巨大的,后面3种方法都是第一种的衍生,在第一种方法效率低下的基础上改进的,这里主要就是一个效率问题了,当然如果你数据量都很小,我想4种方法就代码量和复杂程度来说第1种基本方法就可以了,或当你的硬件非常牛逼了,那再差的方法也可以高效的完成也没有探讨的实际意义了,呵呵说远了,本文主要是在不考虑硬件或同等硬件条件下单从软件角度出发探讨较好的解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值