java大數據如何導出大於65535的文件


我們都知道,一個xls表格的最大存儲的容量是65535條數據。如果大於這個量就會報錯,然后現實中往往需要幾十萬的下載,那么如何解決這個問題,今天我們就從兩種玩法,開始,第一種,。就是下載量小於65535的時候,

 

廢話不多說,直接擼代碼

一:導出功能條數小於65535的時候,可以直接使用

依賴包,自己下載

<!-- 導出用-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
<!-- alibaba easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>1.1.2-beta5</version>
</dependency>

 

工具類:

POIExportUtils:
package com.hbg.dms.util;

import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.hbg.dms.annotations.Excel;
import com.hbg.dms.exception.DmsException;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * @author huojg.tu
 * @version 1.0
 * @date 2020/9/10 13:07
 */
@Slf4j
public class POIExportUtils {

    public static <T> ByteArrayOutputStream export(Class<T> objClass, List<T> dataList) {
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
            Class excelClass = Class.forName(objClass.toString().substring(6));
            Method[] methods = excelClass.getMethods();
            Map<Integer, String> mapCol = new TreeMap<>();
            Map<Integer, String> mapMethod = new TreeMap<>();

            for (Method method : methods) {
                Excel excel = method.getAnnotation(Excel.class);
                if (excel != null) {
                    mapCol.put(excel.order(), excel.colName());
                    mapMethod.put(excel.order(), method.getName());
                }
            }
            HSSFWorkbook wb = new HSSFWorkbook();
            if(dataList.size()>0) {
                poiBuildBody(poiBuildHead(wb,  "sheet1", mapCol), excelClass, mapMethod, dataList);
                wb.write(outputStream);
            }
            return outputStream;
        } catch (Exception e) {
            log.error("導出Excel異常", e);
            throw new DmsException("導出失敗");
        }
    }

    public static <T>  ByteArrayOutputStream export1(Class<T> objClass, List<T> dataList) {

        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            Method[] methods = objClass.getMethods();
            Map<Integer, String> mapCol = new TreeMap<>();
            Map<Integer, String> mapMethod = new TreeMap<>();

            for (Method method : methods) {
                Excel excel = method.getAnnotation(Excel.class);
                if (excel != null) {
                    mapCol.put(excel.order(), excel.colName());
                    mapMethod.put(excel.order(), method.getName());
                }
            }
            // 通過工具類創建writer,默認創建xls格式
            ExcelWriter writer = ExcelUtil.getBigWriter();
            //創建xlsx格式的
            //ExcelWriter writer = ExcelUtil.getWriter(true);
            // 一次性寫出內容,使用默認樣式,強制輸出標題
            writer.writeHeadRow(mapCol.values());
            writer.write(dataList);
            //out為OutputStream,需要寫出到的目標流
            writer.flush(outputStream);
            // 關閉writer,釋放內存
            writer.close();
            return outputStream;
        } catch (Exception e) {
            log.error("導出Excel異常", e);
            throw new DmsException("導出失敗");
        }
    }

    public static HSSFSheet poiBuildHead(HSSFWorkbook wb, String sheetName, Map<Integer, String> mapCol) {
        HSSFSheet sheet01 = wb.createSheet(sheetName);
        HSSFRow row = sheet01.createRow(0);
        HSSFCell cell;
        int i = 0;
        for (Map.Entry<Integer, String> entry : mapCol.entrySet()) {
            cell = row.createCell(i++);
            cell.setCellValue(entry.getValue());
        }
        return sheet01;
    }

    public static <T> void poiBuildBody(HSSFSheet sheet01, Class<T> excelClass, Map<Integer, String> mapMethod, List<T> dataList) throws Exception {
        HSSFRow r = null;
        HSSFCell c = null;

        if (dataList != null && dataList.size() > 0) {
            for (int i = 0; i < dataList.size(); i++) {
                r = sheet01.createRow(i + 1);
                //r.setHeightInPoints(25);
                int j = 0;
                for (Map.Entry<Integer, String> entry : mapMethod.entrySet()) {
                    c = r.createCell(j++);
                    Object obj = excelClass.getDeclaredMethod(entry.getValue()).invoke(dataList.get(i));
                    c.setCellValue(obj == null ? "" : obj + "");
                }
            }
        }
    }





}

如何使用:

首頁根據你需要的傳入分頁參數

然后根據mybaties-plus 中分頁查詢出你需要的list ,

然后調用上面的工具類,直接使用,導出你需要的電子表格xls的格式文件。

    request.setPageNo(1);
       request.setPageSize(1000000000);
       IPage<AgentMemberRes> agentList = this.getAgentList(request);
       List<AgentExportMember> dataList = BeanCopyUtil.copyList(agentList.getRecords(), AgentExportMember.class);
       POIExportUtils.export(AgentExportMember.class, dataList, response, "代理列表" + DateUtil.formatDate(new Date()));

 

 

二:如果下載的量大於65535條數的時候,我們玩的思想:

用spring 的監聽器來調用,和執行程序  ,然后把文件的內容存儲到OSS阿里雲上面生成zip壓縮模式,然后在數據庫把下載鏈接放到數據庫中,供下載中心使用

因此我們需要創建一個監聽器模式:

import com.hbg.dms.model.bo.ExportBO;
import com.hbg.dms.model.dto.base.BasePageRequest;
import org.springframework.context.ApplicationEvent;

/**
 * 導出事件
 * @author PC
 */
public class ExportEvent<Request extends BasePageRequest, Response, ExportResp> extends ApplicationEvent {
    private final ExportBO<Request, Response, ExportResp> exportBO;
    public ExportEvent(Object source) {
        super(source);
        exportBO= (ExportBO)source;
    }

    public ExportBO<Request, Response, ExportResp> getExportBO() {
        return exportBO;
    }


}

 

核心代碼:這個就是我們spirng監聽器里面執行的我們大數據量出來模式

package com.hbg.dms.listener;

import cn.hutool.core.date.DatePattern;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hbg.dms.constant.UploadPathConstants;
import com.hbg.dms.controller.rpc.FileDownloadRpc;
import com.hbg.dms.enums.OssFileTypeEnum;
import com.hbg.dms.model.bo.ExportBO;
import com.hbg.dms.model.dto.base.BasePageRequest;
import com.hbg.dms.model.dto.base.JsonResult;
import com.hbg.dms.model.request.FileDownloadAddRequest;
import com.hbg.dms.model.request.FileDownloadUpdateRequest;
import com.hbg.dms.util.AliOssUtil;
import com.hbg.dms.util.BeanCopyUtil;
import com.hbg.dms.util.POIExportUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 歸檔監聽器
 * @author huojg
 */
@Slf4j
@Component
public class ExportListener<Request extends BasePageRequest,Response, ExportResp> {
    private final Logger logger = LoggerFactory.getLogger(ExportListener.class);

    @Resource
    private FileDownloadRpc fileDownloadRpc;

    @Resource
    private AliOssUtil aliOssUtil;

    @EventListener
    @Async("taskExecutor")
    public void onExportEvent(ExportEvent<Request, Response, ExportResp> event) {
        ExportBO<Request, Response, ExportResp> exportBO = event.getExportBO();
        //文件待下載,生成記錄到數據庫
        FileDownloadAddRequest request = new FileDownloadAddRequest();
        request.setContentDesc(exportBO.getFileName());
        // 文件格式 1 excel; 2 :zip
        if (exportBO.getCompress()) {
            request.setFileFormat(2);
        } else {
            request.setFileFormat(1);
        }

        request.setBackUserId(exportBO.getBackUserId());
        request.setBackUserName(exportBO.getBackUserName());
        // 文件來源 hbg:惠啵購, dms:經銷商管理系統
        request.setSourceFrom("dms");
        FileDownloadUpdateRequest updateRequest = new FileDownloadUpdateRequest();
        //try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zipOutputStream = null;
        try {
            zipOutputStream = new ZipOutputStream(outputStream);
            //調用HBG服務保存記錄
            JsonResult<Integer> saveResult = fileDownloadRpc.save(request);
            updateRequest.setId(saveResult.getData());

            //獲取數據,同實例 串行導出,避免內存占用過大
            synchronized (this) {
                byte[] byteArr = getDataList(exportBO, zipOutputStream);
                try {
                    //需要先關閉zip,后關閉outputStream
                    zipOutputStream.flush();
                    zipOutputStream.close();
                    outputStream.close();
                } catch (IOException e) {
                    logger.error("FileListener.onFileEvent" + e.getMessage(), e);
                }
                String fileName;
                if (exportBO.getCompress()) {
                    byteArr = outputStream.toByteArray();
                    fileName = exportBO.getFileName() + "_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern(DatePattern.PURE_DATETIME_PATTERN)) + ".zip";
                } else {
                    fileName = exportBO.getFileName() + "_" + LocalDateTime.now().format(DateTimeFormatter.ofPattern(DatePattern.PURE_DATETIME_PATTERN)) + exportBO.getSuffix();
                }
                String downloadUrl = aliOssUtil.uploadFile(OssFileTypeEnum.DOCUMENT, byteArr, exportBO.getSubPath(), fileName);
                updateRequest.setDownloadUrl(downloadUrl);
                updateRequest.setFileStatus(1);
            }
            //調用HBG服務 更新狀態
            fileDownloadRpc.updateStatus(updateRequest);
        } catch (Exception e) {
            logger.error("ExportListener.onExportEvent" + e.getMessage(), e);
            if (updateRequest.getId() != null) {
                updateRequest.setFileStatus(2);
                fileDownloadRpc.updateStatus(updateRequest);
            }
        }
    }

    /**
     * 抽象數據獲取方法 , 並且寫入到流
     */
    private byte[] getDataList(ExportBO<Request, Response, ExportResp> exportBO, ZipOutputStream zipOutputStream) throws IOException {
        if (exportBO.getBatchSize() == null || exportBO.getBatchSize() <= 0) {
            exportBO.setBatchSize(10_000);
        }
        //一次最多查詢50000條
        if (exportBO.getBatchSize() > 50_000) {
            exportBO.setBatchSize(50_000);
        }
        exportBO.getParamRequest().setPageSize(exportBO.getBatchSize());
        IPage<Response> dataPage;
        int index = 0;
        //是否應該繼續
        boolean shouldContinue;
        do {
            if (exportBO.getMaxPage() != null && exportBO.getMaxPage() < 1) {
                logger.error("用戶設置的最大頁數小於1,不查詢數據");
                break;
            }
            exportBO.getParamRequest().setPageNo(++index);
            //分頁查詢
            dataPage = exportBO.getDataSupplierFunc().apply(exportBO.getParamRequest());
            if (dataPage.getRecords().size() > 0) {
                List<ExportResp> exportResponseList = BeanCopyUtil.copyList(dataPage.getRecords(), exportBO.getExportRespClass());
                ByteArrayOutputStream outputStream = POIExportUtils.export(exportBO.getExportRespClass(), exportResponseList);
                byte[] byteArray = outputStream.toByteArray();
                //選擇不壓縮,則只查詢一次
                if (!exportBO.getCompress()) {
                    return byteArray;
                }
                zipAppend(byteArray, exportBO.getFileName(), zipOutputStream, index);
            }
            shouldContinue = dataPage.getPages() > dataPage.getCurrent() && (exportBO.getMaxPage() == null || exportBO.getMaxPage() > dataPage.getCurrent());
        } while(shouldContinue);
        return null;
    }

    //壓縮
    protected static void zipAppend(byte[] byteArray, String zipEntityName, ZipOutputStream zipStream, int index) throws IOException {
        if(byteArray!=null && byteArray.length > 0){
            ZipEntry zipEntry = new ZipEntry(new String(zipEntityName.getBytes(), StandardCharsets.UTF_8) + "_" +index +".xls");
            zipStream.putNextEntry(zipEntry);
            zipStream.write(byteArray);
            zipStream.closeEntry();
        }
    }

}

 

如何使用:

  ExportBO<MemberSearchReq, AgentMemberRes, AgentMemberExportRes> exportBO = new ExportBO<>();
       exportBO.setParamRequest(request);
       exportBO.setDataSupplierFunc(this::getAgentList);
       exportBO.setBatchSize(50000);
       exportBO.setExportRespClass(AgentMemberExportRes.class);
       exportBO.setFileName("代理列表");
       exportBO.setSubPath(UploadPathConstants.MEMBER_LIST);
       //exportBO.setCompress(true);
       //exportBO.setSuffix(".xls");
       // 發布導出事件,等待異步導出
       publisher.publishEvent(new ExportEvent<>(exportBO));

 

定義實體類:

package com.hbg.dms.model.bo;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.api.R;
import com.hbg.dms.constant.UploadPathConstants;
import com.hbg.dms.model.dto.base.BasePageRequest;
import io.micrometer.core.lang.Nullable;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import org.apache.poi.ss.formula.functions.T;

import java.util.List;
import java.util.function.Function;

/**
 * 抽象的導出事件對象
 * @author zxf
 * @date 2019/11/11 14:48
 */
@Getter
@Setter
public class ExportBO<Request extends BasePageRequest, Response, ExportResp> {

    /**
     *  數據來源入參參數
     */
    private Request paramRequest;

    /**
     * 數據來源方法:用於獲取數據
     * 一般使用分頁后台管理對應的分頁接口:需要保證只有一個入參
     */
    private Function<Request, IPage<Response>> dataSupplierFunc;

    /**
     * 需要構建一個導出用的對象,
     * 程序將使用 {@link com.hbg.dms.annotations.Excel} 來獲取excel的列標題和順序
     * 轉換的數據導出對象
     */
    private Class<ExportResp> exportRespClass;

    /**
     * 可選參數
     * 批量導出,每次查詢的數據條數
     * 注意:如果選擇了不壓縮,那么只會導出第一頁的數據,全部導出需要設置比較大的size
     * 適用於數據量小於50000的數據
     * 大於5w的強制只查詢5W
     *
     */
    private Integer batchSize = 10000;

    /**
     * 可選參數
     * 希望導出的最大頁數
     * 為空或小於1則查詢全部
     */
    private Integer maxPage;

    /**
     * 文件名,建議帶正確的后綴
     */
    private String fileName;

    /**
     * 可選參數
     * 文件名后綴,帶點號
     * eg: .xls
     * 默認 .xlsx
     */
    private String suffix = ".xls";

    /**
     * 可選參數
     * 是否壓縮
     * 默認 是
     *
     */
    private Boolean compress = true;

    /**
     * 可選參數
     * 使用子路徑,會生成子路徑文件夾,作為更細的划分
     * @see UploadPathConstants
     */
    private String subPath;


    /**
     * dms獲取不到session的用戶信息,需要前端傳遞此參數
     * 后台操作的用戶Id
     */
    private Long backUserId;

    /**
     * dms獲取不到session的用戶信息,需要前端傳遞此參數
     * 操作人姓名
     */
    private String backUserName;


}
package com.hbg.dms.model.dto.base;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;


@ApiModel("分頁參數")
public class BasePageRequest {

    @ApiModelProperty(value = "當前頁碼", required = true)
    private Integer pageNo;

    @ApiModelProperty(value = "每頁條數", required = true)
    private Integer pageSize;

    public int getPageNo() {
        if(pageNo == null || pageNo <= 0){
            pageNo = 1;
        }
        return pageNo;
    }

    public int getPageSize() {
        if(pageSize == null  || pageSize <= 0){
            pageSize = 10;
        }
        return pageSize;
    }

    @ApiModelProperty(hidden = true)
    public <T> Page<T> getPage() {
        return new Page<>(getPageNo(), getPageSize());
    }


    public void setPageNo(Integer pageNo) {
        this.pageNo = pageNo;
    }

    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }
}

通過上面的定義實體類:

ExportBO

我們通過執行分頁的list集合。處理代碼

參數解析:

request:請求參數
this::getAgentList:查詢分頁出來的list
AgentMemberExportRes.class:導出的實體類
setSubPath:路徑名稱
publishEvent:發送通知

Excel:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Excel {
    /**
     * 列名稱
     * @return
     */
    String colName();

    /**
     * 順序
     * @return
     */
    int order();
}
導出的實體類上面,這樣寫:
 
   @Excel(colName = "上級id", order = 1)
    public Integer getSupperId() {
        return supperId;
    }

 

 

這樣可以導出,幾十萬的是數據量。

 

帶分頁--大數據量導出的功能就此完成--你們的精華,就是,監聽器實現的代碼。同學看不懂的,可以聯系我要源碼。收費10元

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM