微服务超大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);
    }
}

效果图:

相关推荐
琪露诺大湿2 分钟前
网页聊天系统——测试报告
java·软件测试·功能测试·websocket·html·项目·测试报告
小园子的小菜5 分钟前
生产实战|冷热数据转换:从识别、触发到落地全流程解析
java·开发语言·spring
开开心心就好7 分钟前
一款既是直播工具又是浏览器的软件
linux·运维·服务器·智能手机·逻辑回归·excel·最小二乘法
invicinble9 分钟前
对于线程的思路
java
iwS2o90XT18 分钟前
WebSocket编程:Java实现实时双向通信应用
java·websocket·网络协议
Highcharts.js21 分钟前
技术组合分析:Highcharts 的数据集成能力解析
java·前端·金融·echarts·saas·bi·highcharts
shaoFan11 小时前
关于java 调用阿里千问大模型,流式返回,并返回给前端
java·前端·状态模式
雪碧聊技术1 小时前
Java历史—沙箱安全机制
java·安全·沙箱机制
java1234_小锋1 小时前
Spring AI 2.0 开发Java Agent智能体 - Spring AI 2.0简介
java·人工智能·spring·spring ai
Hesionberger1 小时前
LeetCode72.编辑距离(多维动态规划)
java·开发语言·c++·python·算法