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元

浙公网安备 33010602011771号