微服务超大Excel文件导出方案优化

1、在导出Excel时经常会碰到文件过大,导出特别慢

2、微服务限制了请求超时时间,文件过大情况必然超时

优化思路:

1、文件过大时通过文件拆分、打包压缩zip,然后上传到oss,并设置有效期(30天过期)

2、把同步下载改成异步

3、文件生成完成,更新任务状态

4、通过oss的下载链接下载文件

5、多模块复用,只需要实现各自查询Excel业务数据的接口即可

以下是部分参考代码:

java 复制代码
public class TxnETExecutor extends AbstractExportTaskExecutor {
    private final MposScene mposScene;
    private final OssService ossService;

    @Override
    public boolean support(int taskType) {
        return ExportTaskType.MPOS_TXN_BILL_EXPORT.ordinal() == taskType;
    }

    @Override
    public int getFileSize(String reqBody, String audClientId, Long groupId) {
        TxnMposBillItemFindReq req = JSON.parseObject(reqBody, TxnMposBillItemFindReq.class);
        req.setAudClientId(audClientId);
        req.setLoginGroupId(groupId);
        req.setPage(1);
        req.setPageSize(1);
        req.setDiffStatus(true);
        Pageable pageable = mposScene.findByChannelBillItem(req);
        return pageable.getTotalPages().intValue();
    }

    public void execute(Long taskId, int fileSize, String reqBody, String audClientId, Long groupId) {
        TxnMposBillItemFindReq req = JSON.parseObject(reqBody, TxnMposBillItemFindReq.class);
        req.setAudClientId(audClientId);
        req.setLoginGroupId(groupId);
        req.setPage(1);
        req.setDiffStatus(true);
        int pageSize = Math.min(fileSize, DEFAULT_PAGE_SIZE);
        req.setPageSize(pageSize);
        Pageable pageable = mposScene.findByChannelBillItem(req);
        Long totalPages = pageable.getTotalPages();
        List<Map<String, Object>> first = parse(pageable.getItems());
        ByteArrayOutputStream outputStream = WebFluxUtils.buildExcelOutputStream(first, null);
        if (totalPages <= 1) {
            OssPutObjectResp objectResp = upload(audClientId, "xlsx", new ByteArrayInputStream(outputStream.toByteArray()));
            String fileUrl = objectResp.getFileUrl();
            update(CashierExportTaskUpdateReq.newBuilder().setId(taskId).setFileStatus(ExportTaskFileStatus.SUCCESS.ordinal()).setFileFormat("xlsx").setFileProgressRate("100%").setFileUrl(fileUrl).build());
            return;
        }
        Map<String, ByteArrayOutputStream> files = new HashMap<>();
        files.put(String.format("渠道对账单[%s-%s].xlsx", 1, totalPages), outputStream);
        for (int i = 2; i <= totalPages; i++) {
            req.setPage(i);
            Pageable pageableExportMore = mposScene.findByChannelBillItem(req);
            ByteArrayOutputStream j = WebFluxUtils.buildExcelOutputStream(parse(pageableExportMore.getItems()), null);
            files.put(String.format("渠道对账单[%s-%s].xlsx", i, totalPages), j);
        }
        Path tmp = zip(files);
        if (tmp == null) {
            update(CashierExportTaskUpdateReq.newBuilder().setId(taskId).setFileStatus(ExportTaskFileStatus.FAIL.ordinal()).setFileFormat("zip").setFileProgressRate("100%").build());
            return;
        }
        InputStream inputStream;
        try {
            inputStream = Files.newInputStream(tmp);
        } catch (IOException e) {
            e.printStackTrace();
            update(CashierExportTaskUpdateReq.newBuilder().setId(taskId).setFileStatus(ExportTaskFileStatus.FAIL.ordinal()).setFileFormat("zip").setFileProgressRate("100%").build());
            return;
        }
        try {
            OssPutObjectResp objectResp = upload(audClientId, "zip", inputStream);
            update(CashierExportTaskUpdateReq.newBuilder().setId(taskId).setFileStatus(ExportTaskFileStatus.SUCCESS.ordinal()).setFileFormat("zip").setFileProgressRate("100%").setFileUrl(objectResp.getFileUrl()).build());
        } catch (Exception e) {
            e.printStackTrace();
            update(CashierExportTaskUpdateReq.newBuilder().setId(taskId).setFileStatus(ExportTaskFileStatus.FAIL.ordinal()).setFileFormat("zip").setFileProgressRate("100%").build());
        } finally {
            try {
                Files.delete(tmp);
            } catch (IOException ignore) {
            }
        }


    }

    private List<Map<String, Object>> parse(Collection<TxnMposBillItemFindResp> items) {
        List<Map<String, Object>> result = new ArrayList<>();
        if (items == null || items.isEmpty()) {
            throw FibException.ofNotFound("暂无数据");
        }
        items.forEach(item -> {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("创建时间", item.getCreateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            map.put("批次号", item.getBatchNo());
            map.put("交易时间", item.getTransTime());
            map.put("交易月份", item.getTransMonth());
            map.put("平台订单号", item.getServerOrderId());
            ......
            result.add(map);
        });
        return result;
    }

    private OssPutObjectResp upload(String audClientId, String suffix, InputStream inputStream) {
        return ossService.putObject(OssPutObjectReq.newBuilder()
                .setBucketName(DEFAULT_BUCKET)
                .setAudClientId(audClientId)
                .setDirectoryPath("***")
                .setSuffix(suffix)
                .build(), inputStream);
    }
}

效果图:

相关推荐
聪明的笨猪猪8 分钟前
Java Spring “IOC + DI”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
ThisIsMirror11 分钟前
CompletableFuture并行任务超时处理模板
java·windows·python
珹洺1 小时前
Java-Spring入门指南(二十一)Thymeleaf 视图解析器
java·开发语言·spring
源码集结号1 小时前
一套智慧工地云平台源码,支持监管端、项目管理端,Java+Spring Cloud +UniApp +MySql技术开发
java·mysql·spring cloud·uni-app·源码·智慧工地·成品系统
EnCi Zheng1 小时前
Spring Security 最简配置完全指南-从入门到精通前后端分离安全配置
java·安全·spring
程序员小假1 小时前
为什么这些 SQL 语句逻辑相同,性能却差异巨大?
java·后端
泉城老铁1 小时前
导出大量数据时如何优化内存使用?SXSSFWorkbook的具体实现方法是什么?
spring boot·后端·excel
泉城老铁2 小时前
springboot实现对接poi 导出excel折线图
java·spring boot·后端
金銀銅鐵2 小时前
[Java] 如何自动生成简单的 Mermaid 类图
java·后端
纵横八荒2 小时前
Java基础加强13-集合框架、Stream流
java·开发语言