easyExcel导出百万数据

先说一下需求场景,导出接口不能直接查询数据源,是通过接口远程调用,要求支持动态表头,需要支持导出200w以上数据.设计如下:
1.由于数据远程调用,大量数据查询导出不符合实际情况,这里采取远程调用接口,将数据写入华为obs,使用追加写入的方式,可以分批查询写入,避免一次查询全部数据造成的内存溢出问题,而且可以边写边读,提高效率
2.设计一个进度表记录写入obs文件的进度,导出接口查询进度表,实时读取写入obs的数据
3.当obs写入完成修改写入状态,导出接口发现写入完成后进行最后一次数据读取,然后讲获取的数据导出到excel
try {
String objectKey = EXPORT_LOG_PATH + operationId + “.log”;
String fileName = getFileName(areaMark, didSegment);

servletResponse.setCharacterEncoding("UTF-8");
servletResponse.setHeader("content-Type", "application/vnd.ms-excel");
servletResponse.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "UTF-8"));
servletResponse.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
// 动态表头字段
List<String> columns = getColumns(deviceModelName, deviceType, dType, channel, version, p2pVersion, shareAbility,
        storeAbility, msgAbility, operateAbility, aiAbility, pushAbility);
ExcelWriter build = EasyExcel.write(servletResponse.getOutputStream(), DeviceInfoObs.class).build();
Integer exportCount = 0;
// sheet页
Integer sheetNum = 1;
// 每页最大条数
Integer sheetSize = 1000000;
// 写入状态
String writeStatus = "running";
// 上次读取位置
Long lastPosition = 0L;
long timeBegin = System.currentTimeMillis();
while ("running".equals(writeStatus)) {
    // 查询设置时间间隔 1s   等待设备信息写入OBS
    Thread.sleep(1000);
    if (System.currentTimeMillis() - timeBegin >= 3600000) {
        LOG.error("=======deviceExport===本次下载超时主动退出,operationId={}", operationId);
        break;
    }
    Map<String, Object> progress = getProgress(areaMark, operationId);
    // 写入状态
    if (progress != null && progress.get("writeStatus") != null) {
        writeStatus = progress.get("writeStatus").toString();
    }
    // 追加位置
    List<String> posList = new ArrayList<>();
    if (progress != null && progress.get("appendPos") != null) {
        String appendPos = progress.get("appendPos").toString();
        if (lastPosition != 0L) {
            appendPos = appendPos.substring(appendPos.indexOf(String.valueOf(lastPosition)));
        }
        posList = Arrays.asList(appendPos.split(","));
    }
    if (posList.size() > 1) {
        // 记录读取obs时最后一个pos
        lastPosition = Long.parseLong(posList.get(posList.size() - 1));
        for (int i = 0; i < posList.size() - 1; i++) {
            List<DeviceInfoObs> deviceInfoObsList = getAppendLog(Long.parseLong(posList.get(i)),
                    Long.parseLong(posList.get(i + 1)), objectKey);
            // 组装数据
            setExportData(deviceInfoObsList);
            exportCount += deviceInfoObsList.size();
            sheetNum = (exportCount + sheetSize - 1) / sheetSize;
            // 写入数据
            build.write(deviceInfoObsList,
                    EasyExcel.writerSheet("sheet" + sheetNum).includeColumnFiledNames(columns).build());
            // 更新导出进度
            updateProgress(areaMark, operationId, exportCount);
        }
    }
}
build.finish();

} catch (Exception e) {
LOG.error(“deviceExport error:{}”, e.getMessage());
e.printStackTrace();
}
try {
// 查询所有分表
List allTable = deviceInfoDOMapper.getTableList();
tableList.addAll(allTable);

// 创建obs连接
ObsClient obsClient = ObsClientLoad.getInstance(accesskey, secretkey, endPoint);
// 首次写入标识
boolean fistFlag = true;
String objectKey = EXPORT_LOG_PATH + operationId + ".log";
// 存放设备数据的集合 批处理减少写obs的次数
List<DeviceInfoObs> partDeviceInfoList = new ArrayList<>();
// 查询分表数据
for (String table : tableList) {
    List<DeviceInfoObs> deviceInfos = deviceInfoDOMapper.getDeviceInfoList(table, deviceModelName, deviceType,
            dType, channel, version, p2pVersion, shareAbility, storeAbility, msgAbility, operateAbility,
            aiAbility, pushAbility, null);
    while (deviceInfos.size() > 0) {
        partDeviceInfoList.addAll(deviceInfos);
        Long id = deviceInfos.get(deviceInfos.size() - 1).getId();
        if (partDeviceInfoList.size() >= BATCH_WRITE_SIZE) {
            // 分表数据写入obs
            Long position = writDidToObs(partDeviceInfoList, obsClient, fistFlag, objectKey);
            // 同步更新写入进度
            fistFlag = updateWriteProgress(partDeviceInfoList, fistFlag, operationId, position);
        }
        // 继续查询
        deviceInfos = deviceInfoDOMapper.getDeviceInfoList(table, deviceModelName, deviceType, dType, channel,
                version, p2pVersion, shareAbility, storeAbility, msgAbility, operateAbility, aiAbility, pushAbility, id);
    }
}
// 处理数据不满足批量上传数量的情况
if (partDeviceInfoList.size() > 0) {
    // 分表数据写入obs
    Long position = writDidToObs(partDeviceInfoList, obsClient, fistFlag, objectKey);
    // 同步更新写入进度
    updateWriteProgress(partDeviceInfoList, fistFlag, operationId, position);
}
// 设置对象过期时间
setObsExpires(obsClient, objectKey, "1");
// 设置写入状态成功
DeviceInfoProgress progress = new DeviceInfoProgress();
progress.setOperateId(operationId);
progress.setWriteStatus("success");
deviceInfoProgressMapper.updateByPrimaryKeySelective(progress);

}catch (Exception e){
e.printStackTrace();
log.error(“deviceExport: " + e.getMessage());
DeviceInfoProgress progress = new DeviceInfoProgress();
progress.setOperateId(operationId);
progress.setWriteStatus(“failed”);
deviceInfoProgressMapper.updateByPrimaryKeySelective(progress);
}
String property = System.getProperty(“line.separator”);
Long position = 0L;
try {
// 首次写入
if (fistFlag) {
AppendObjectRequest objectRequest = new AppendObjectRequest();
objectRequest.setObjectKey(objectKey);
objectRequest.setPosition(0L);
objectRequest.setBucketName(bucketName);
// 写入文件内容
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
for (DeviceInfoObs info : list) {
byteArrayOutputStream.write(JSONObject.toJSONString(info).getBytes());
if (list.indexOf(info) < list.size() - 1) {
byteArrayOutputStream.write(”,“.getBytes());
byteArrayOutputStream.write(property.getBytes());
}
}
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectRequest.setInput(byteArrayInputStream);
// 追加记录
obsClient.appendObject(objectRequest);
// 返回写入位置
ObsObject object = obsClient.getObject(bucketName, objectKey);
position = object.getMetadata().getContentLength();
} else {
// 追加操作
ObsObject object = obsClient.getObject(bucketName, objectKey);
AppendObjectRequest objectRequest = new AppendObjectRequest();
objectRequest.setObjectKey(objectKey);
objectRequest.setPosition(object.getMetadata().getContentLength());
objectRequest.setBucketName(bucketName);
// 追加写入文件内容
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
for (DeviceInfoObs info : list) {
byteArrayOutputStream.write(”,".getBytes());
byteArrayOutputStream.write(property.getBytes());
byteArrayOutputStream.write(JSONObject.toJSONString(info).getBytes());
}
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectRequest.setInput(byteArrayInputStream);
// 追加记录
obsClient.appendObject(objectRequest);
// 返回写入位置
object = obsClient.getObject(bucketName, objectKey);
position = object.getMetadata().getContentLength();
}
} catch (Exception e) {
e.printStackTrace();
log.error(“writDidToObs error msg is {}”, e.getMessage());
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/610865.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

螺栓扭矩如何设计?——SunTorque智能扭矩系统

螺栓扭矩设计的大小是一个涉及工程实践的重要问题&#xff0c;它直接关系到螺栓连接的紧固质量和安全性。螺栓扭矩是工程领域中常用的一个概念&#xff0c;用来描述螺栓在连接过程中所需的旋转力矩。正确的螺栓扭矩可以确保螺栓和螺母之间的紧密连接&#xff0c;避免由于松动而…

Java入门基础学习笔记10——变量

变量的学习路径&#xff1a; 认识变量->为什么要用变量&#xff1f;->变量有啥特点&#xff1f;->变量有啥应用场景&#xff1f; 什么是变量&#xff1f; 变量是用来记住程序要处理的数据的。 变量的定义格式&#xff1a; 数据类型 变量名称 数据&#xff1b; 数…

C语言/数据结构——(用双链表实现数据的增删查改)

一.前言 嗨嗨嗨&#xff0c;大家好久不见&#xff01;前面我们已经通过数组实现数据的增删查改、单链表实现数据的增删查改&#xff0c;现在让我们尝试一下使用双链表实现数据的增删查改吧&#xff01; 二.正文 如同往常一样&#xff0c;对于稍微大点的项目来说&#xff0c;…

2024洗地机选购指南 | 怎么选洗地机不会被坑?

家里的地板总是需要打扫&#xff0c;但工作忙碌的我们往往没有足够的时间来打理。洗地机不仅能够帮助我们节省宝贵的时间&#xff0c;还能让我们的家变得一尘不染。今天&#xff0c;笔者将为大家讲讲挑选洗地机的技巧&#xff0c;告诉大家怎么挑选洗地机不会被坑&#xff0c;顺…

解锁楼宇自动化新维度西门子Insight+BACnet IP I/O控制器

数字城市的楼宇自动化已不再是一个遥不可及的概念&#xff0c;而是成为了现代建筑的标配。特别是在大型商业综合体、高端写字楼和公共设施中&#xff0c;高效的楼宇管理系统是确保环境舒适度与能源效率的关键。当提及楼宇自动化领域的佼佼者&#xff0c;西门子Insight楼宇自动化…

【洛谷】动态规划之最长公共子序列

前言&#xff1a; 本系列目的是记录日常所刷的题&#xff0c;有的是自己想出来的题&#xff0c;有的是看了大佬题解后想明白的题 题目 P1439 【模板】最长公共子序列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 前提&#xff1a; 两个排列都是1到n的排列&#xff0c;说…

如何使用Vite快速构建vue项目

1、在自己定义的目录下打开cmd命令窗口&#xff1a;如文件夹目录上面输入cmd回车就可以打开 2、检查 node环境&#xff1a;通过node --version看版本号表示安装好了 3、 使用Vite 快速构建Vue项目 npm init vitelatest qiuqiu.admin 注意&#xff1a;如何你电脑没有装vite首…

《2024网络安全报告》中文版

Check Point发布了《2024 年网络安全报告》&#xff0c;Check Point Research 对网络攻击数据&#xff08;包括所有地区和全球的统计数据&#xff09;进行了全面分析&#xff0c;揭示了不断变化的网络威胁形势。 ● 去年&#xff0c;全球 1/10 的机构遭遇勒索软件攻击尝试&a…

QTday5

目录 网络聊天室服务器 网络聊天室客户端 学生管理系统 网络聊天室服务器 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);//实例化一个tcp服务器sernew QTcpSe…

【SpringBoot记录】自动配置原理(2):自动配置的案例说明

案例说明 通过前面案例可以很明显感受到&#xff0c;SpringBoot除了做了依赖管理&#xff0c;还帮我们做了一些常规的配置&#xff0c;下面举例详细说明。 tomcat 项目可以直接启动&#xff0c;不需要任何配置&#xff0c;通过上一节发现引入了启动器中引入了spring-boot-st…

Python之数据分析基础

导言&#xff1a; “21世纪的竞争是数据的竞争&#xff0c;谁掌握数据&#xff0c;谁就掌握未来”。如何将大量看似杂乱无章的数据进行聚合&#xff0c;并发现潜在的规律也变得越来越重要。本文将先说明数据分析的步骤&#xff0c;再通过python完成实例数据的处理、分析最终展…

【初阶数据结构】顺序表OJ题讲解

前言 &#x1f4da;作者简介&#xff1a;爱编程的小马&#xff0c;正在学习C/C&#xff0c;Linux及MySQL。 &#x1f4da;本文收录与初阶数据结构系列&#xff0c;本专栏主要是针对时间、空间复杂度&#xff0c;顺序表和链表、栈和队列、二叉树以及各类排序算法&#xff0c;持…

PyTorch 图像篇

计算机视觉技术是一门包括计算机科学与工程、神经生理学、物理学、信号处理、认知科学、应用数学与统计等多学科的综合性科学技术&#xff0c; 是人工智能的一个重要分支&#xff0c; 目前在智能安防、自动驾驶汽车、医疗保健、生成制造等领域具有重要的应用价值。 计算机视觉…

常用控件(一)

常用控件 一 按钮类控件QPushButtonQRadioButtonQCheckBox 按钮类控件 QPushButton 使用QPushButton表示一个按钮&#xff0c;这是我们当前最熟悉的一个控件了; QPushButton继承自QAbstractButton&#xff0c;这个类是个抽象类&#xff0c;是其他按钮类的父类; QAbstractButt…

anaconda虚拟环境pytorch安装

1.先创建conda的虚拟环境 conda create -n gputorch python3.102.激活刚刚创建好的虚拟环境 conda activate gputorch3.设置镜像源 这一步是后面安装pytorch相关包所需要的来源 pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple4.查看电脑的显卡…

跨界内容营销:Kompas.ai如何帮助你的品牌打破行业边界

在当今多元化的市场环境中&#xff0c;跨界营销已成为品牌拓展影响力和用户基础的重要策略。通过跨界合作&#xff0c;品牌能够打破行业界限&#xff0c;创造独特的用户体验&#xff0c;从而提升品牌形象和市场竞争力。本文将深入分析跨界营销的作用&#xff0c;详细介绍Kompas…

SliderCaptcha滑块验证码功能

SliderCaptcha滑块验证码功能 资源文件及文档&#xff1a;https://gitee.com/LongbowEnterprise/SliderCaptcha <!DOCTYPE html> <html lang"en" xmlns:th"http://www.thymeleaf.org"> <head><meta charset"UTF-8"><…

mysql 其他类型转换为BIT

看官网说明,BIT没什么特殊之处。但实际操作却不能将任何其他类型字段转为BIT,下面两个都报语法错误 CAST(column AS BIT(1)) AS aa , CAST(column AS BIT) AS bb, BIT value则模式是VARBINARY b1 as cc, -- cc为VARBINARY类型 下面是《高性能MySQL(第四版)》中关于BIT类型的…

cPanel中如何卸载已安装的SSL证书

我使用的Hostease的Linux虚拟主机产品默认带普通用户权限的cPanel面板&#xff0c;由于临时搭建了一个测试的个人的纯静态的网站&#xff0c;不想要安装SSL证书&#xff0c;但是据这边了解HosteaseLinux虚拟主机是只要域名解析指向主机IP&#xff0c;并且绑定到主机&#xff0c…

centos7.9升级4.19内核

centos默认的内核版本是3.10 通过命令 uname -a 输出系统的详细信息 在部署k8s集群时使用默认的3.10版本的内核&#xff0c;容易出各种奇奇怪怪的问题、可以理解为docker和k8s与该内核版本不兼容&#xff0c;所以在部署k8s集群时&#xff0c;务必要升级内核&#xff0c;这里…
最新文章