POI实现EXCEL大文件生成

1. 场景

需要后台生成excel,数据量可能有30万行以上,使用之前的poi 代码可能会报 OOM。

2. 解决办法

减少内存占用,将生成的excel同步写入本地文件中。
具体实现方式可以使用 easy poi 或者 poi 中的 SXSSF。
easy poi 不够灵活,此处使用的是 SXSSF。

poi 分类

POI提供了HSSF、XSSF以及SXSSF三种方式操作Excel。

HSSF:Excel97-2003版本,扩展名为.xls。一个sheet最大行数65536,最大列数256。
XSSF:Excel2007版本开始,扩展名为.xlsx。一个sheet最大行数1048576,最大列数16384。
SXSSF:是在XSSF基础上,POI3.8版本开始提供的支持低内存占用的操作方式,扩展名为.xlsx。

Excel版本兼容性是向下兼容。

3 . 生成大文件excel

有第一点可知,如果excel数据量很大的话,需要使用 SXSSFWorksheet生成excel,
具体代码如下:

import junit.framework.Assert;
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.ss.util.CellReference;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
   public static void main(String[] args) throws Throwable {
   // 配置SXSSFWorksheet内存中的数据,其余的实时写入本地文件;
   // 100标识内存中存放数据的条数;默认为100;
   // 为-1时表示全部在内存处理;
       SXSSFWorkbook wb = new SXSSFWorkbook(100); // keep 100 rows in memory, exceeding rows will be flushed to disk
       Sheet sh = wb.createSheet();
       for(int rownum = 0; rownum < 1000; rownum++){
           Row row = sh.createRow(rownum);
           for(int cellnum = 0; cellnum < 10; cellnum++){
               Cell cell = row.createCell(cellnum);
               String address = new CellReference(cell).formatAsString();
               cell.setCellValue(address);
           }
       }
       // 小于900 的应该都被放入了本地文件;
       // 所以现在获取不到小于900的行,下面的断言会触发;
       // Rows with rownum < 900 are flushed and not accessible
       for(int rownum = 0; rownum < 900; rownum++){
         Assert.assertNull(sh.getRow(rownum));
       }
       // 大于900 的没有被放入了本地文件;
       // 所以现在可以获取到大于900的行,下面的断言会触发;
       // ther last 100 rows are still in memory
       for(int rownum = 900; rownum < 1000; rownum++){
           Assert.assertNotNull(sh.getRow(rownum));
       }
       FileOutputStream out = new FileOutputStream("/temp/sxssf.xlsx");
       wb.write(out);
       out.close();
       // 销毁本地临时文件
       // dispose of temporary files backing this workbook on disk
       wb.dispose();
   }

参考:官方文档

4. 实例

此处需要将生成的大文件上传导 S3,某些简单方法略。

    @SneakyThrows
    public void executeNoLogoExcelDownload(DownloadCenterEntity downloadCenterEntity) {
        String fileName = downloadCenterEntity.getFileName();
        File file = new File(fileName);
        if (!file.exists()) {
            file.createNewFile();
        }
        try {
            generateNoLogoExcelBytes(fileName);
        } catch (Exception e) {
            e.printStackTrace();
            asyncDownloadEnd(downloadCenterEntity,file,RESULT_FAILED);
            disposeFile(file);
            return;
        }
        asyncDownloadEnd(downloadCenterEntity,file,RESULT_SUCCESS);
        disposeFile(file);
    }
	public void generateNoLogoExcelBytes(String excelUri) {
        logoNumber = 0;
        init();
        workbook = new SXSSFWorkbook(SHEET_WINDOW_SIZE);
        sheet = workbook.createSheet(excelName);
        excelUtil.createSheet(sheet, workbook, excelName, header, data, paramColumnIndexMap, adhocExcelDTO);
        try (FileOutputStream out = new FileOutputStream(excelUri)) {
            workbook.write(out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private void disposeFile(File file) {
        workbook.dispose();
        if (file.exists()) {
            file.delete();
        }
    }
    @SneakyThrows
    @Override
    public void asyncDownloadEnd(DownloadCenterEntity entity,File file,int result) {
        String netUrlPath = S3Utils.upload(file, "/DOWNLOAD/" +file.getName());
        entity.setStatus(result);
        entity.setUrl(netUrlPath);
        downloadCenterService.updateById(entity);
    }
        public static String upload(File file, String s3Path) throws IOException {
        Bucket bucket = conn.createBucket(s3ConfigProperties.getBucketName());
        FileInputStream fis = new FileInputStream(file);
        s3Path = s3Path.replace("\\", "/");
        if (s3Path.indexOf(":") > 0) {
            s3Path = s3Path.substring(s3Path.indexOf(":") + 1);
        }

        if (s3Path.startsWith("/")) {
            s3Path = s3Path.substring(1);
        }

        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentEncoding("UTF-8");
        objectMetadata.setContentLength((long)fis.available());
        objectMetadata.setHeader("x-amz-acl", "public-read");
        conn.putObject(bucket.getName(), s3Path, fis, objectMetadata);
        return s3Path;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值