Ubuntu 22.04 下 CURL(C++) 实现分块上传/下载文件源码

为了帮助大家理解代码,先介绍文件上传/下载流程:

上传文件流程说明:首先向服务器 restful api 接口 /common发送 Post 请求 ,服务器端返回 project guid。读取本地文件,按照给定 chunk_size(例如 10240 byte),不断循环向服务器 restful api 接口 /upload_chunk 发送数据,直到文件传输完成。

视频上传后,在服务器端做 3D 建模,耗时1-2小时,故拆分步骤,首先向服务器发送请求生成 project_guid, “刷新”按钮不停获取服务器状态。这些步骤不是必须,使用者按照自己项目需求取舍。发送请求的 header, body_json 等格式,同样按照自己的项目需求设计即可。

这是创建 project 的接口说明: 

urlhttp://192.168.1.0:8000/common
type

POST

param{
    "model_type":1,
    "project_type":0,
    "object":"plants"
}
return{
  "data": "85c9c7cc-18d6-11ef-818a-7d68b3e42070",
  "errcode": 0
}

下载文件:同样采取分块下载。

CMakeList.txt:

# 查找libcurl组件
find_package(CURL REQUIRED)

include_directories(
  include
  ${PYTHON_INCLUDE_DIRS}
  ${CURL_INCLUDE_DIRS}
)

target_link_libraries(${PROJECT_NAME} PRIVATE "${CURL_LIBRARY}")
target_include_directories(${PROJECT_NAME} PRIVATE "${CURL_INCLUDE_DIR}")

upload调用代码:

void test_restapi_upload_chunk(){std::string project_id = "bee8400e-1f13-11ef-818a-7d68b3e42070";std::string file_path = "/home/coco/Documents/IMG_02391.MOV";std::string url = "http://192.168.1.87:8200/upload_chunk";try {upload_chunk(url, project_id, file_path);std::cout << "Chunk upload completed successfully." << std::endl;} catch (const std::exception& e) {std::cerr << "An error occurred: " << e.what() << std::endl;}
}

 upload_chunk() 函数实现代码:

void upload_chunk(const std::string& url, const std::string& project_id, const std::string& file_path, size_t chunkSize) {// curl初始化CURL* curl = curl_easy_init();if (!curl) {std::cerr << "CURL initialization failed." << std::endl;return;}// 初始化表单和头部curl_httppost* formpost = NULL;curl_httppost* lastptr = NULL;struct curl_slist* headers = NULL;// 设置 URL 和 HTTP POST 选项std::cout << "url.c_str(): " + url << std::endl;curl_easy_setopt(curl, CURLOPT_URL, url.c_str());curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);// 打开文件和读取逻辑std::ifstream file(file_path, std::ios::binary);if (!file.is_open()) {std::cerr << "Failed to open file: " << file_path << std::endl;return;}std::filesystem::path path(file_path);std::string file_name = path.filename();size_t totalSize = std::filesystem::file_size(file_path);size_t offset = 0;size_t start = 0;std::cout << "totalSize: " + std::to_string(totalSize) << std::endl;// 循环读取文件并发送std::vector<char> buffer(chunkSize);while (!file.eof()) {// 读取文件内容到bufferfile.read(buffer.data(), chunkSize);size_t bytesRead = file.gcount();if (bytesRead == 0) {break; // 没有更多数据可读,退出循环}// 构建project表单数据CURLFORMcode form_result = curl_formadd(&formpost,&lastptr,CURLFORM_COPYNAME, "project", // 表单字段名称CURLFORM_COPYCONTENTS, project_id.c_str(), // 表单字段值CURLFORM_END);// 为每个文件块创建一个新的表单form_result = curl_formadd(&formpost,&lastptr,CURLFORM_COPYNAME, "file", // 表单字段名称CURLFORM_BUFFERPTR, buffer.data(), // 缓冲区指针CURLFORM_BUFFERLENGTH, static_cast<long>(bytesRead), // 缓冲区长度CURLFORM_FILENAME, file_name.c_str(), // 文件名CURLFORM_CONTENTTYPE, "application/octet-stream", // 内容类型CURLFORM_END);if (form_result != CURL_FORMADD_OK) {std::cerr << "Error building file form." << std::endl;break;}// 设置请求头和POST数据std::string contentRange = std::to_string(start) + "-" + std::to_string(start + bytesRead - 1) + "/" + std::to_string(totalSize);std::cout << "contentRange: " + contentRange << std::endl;headers = curl_slist_append(headers, ("Content-Range: " + contentRange).c_str());curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);// 设置回调函数和数据std::string responseContent;curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, UploadWriteCallback);curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseContent);// 执行请求CURLcode res = curl_easy_perform(curl);if (res != CURLE_OK) {std::cerr << "CURL error: " << curl_easy_strerror(res) << std::endl;break;}// 更新下一个块的起始位置start += bytesRead;// 处理响应数据if (!responseContent.empty()) {std::cout << responseContent << std::endl;}// 清理headers和表单curl_slist_free_all(headers);headers = nullptr;curl_formfree(formpost);formpost = NULL;lastptr = NULL;}// 清理资源curl_formfree(formpost);curl_easy_cleanup(curl);file.close();
}

download 调用代码:

void test_restapi_download_chunk(){char cwd[PATH_MAX];if (getcwd(cwd, sizeof(cwd)) != NULL) {std::cout << "getcwd(): " << cwd << std::endl;} else {perror("getcwd() error");}QString currentPath = QDir::currentPath();std::cout << "Current working directory: " << currentPath.toStdString() << std::endl;std::string url = "http://192.168.1.87:8200/download_chunk"; // 替换为实际的Flask服务器URLstd::string project = "85c9c7cc-18d6-11ef-818a-7d68b3e42070"; // 替换为实际项目名称std::string output_path = "/home/coco/Downloads/point_cloud.ply"; // 替换为实际保存文件的路径size_t start = 0; // 替换为实际起始字节size_t end = 1024; // 替换为实际结束字节,这里假设DOWNLOAD_RANGE的值为102400try {if (FileSystemUtils::exists(output_path)) {std::cout << output_path + "already exists." << std::endl;return ;}download_chunk(url, project, output_path, start, end);std::cout << "Chunk download completed successfully." << std::endl;} catch (const std::exception& e) {std::cerr << "An error occurred: " << e.what() << std::endl;}
}

 download_chunk()函数代码实现:

int download_chunk(const std::string& url, const std::string& project_id, const std::string& output_path, size_t start, size_t end=10240) {// 创建 curl 句柄CURL *curl = curl_easy_init();if (!curl) {std::cerr << "CURL initialization failed." << std::endl;return 1;}// 保存文件,确保以追加模式打开std::ofstream outfile(output_path, std::ios::binary | std::ios::app);if (!outfile.is_open()) {std::cerr << "Cannot open file: " << output_path << std::endl;return 1;}// 设置请求的URLcurl_easy_setopt(curl, CURLOPT_URL, url.c_str());curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");//curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);//curl_easy_setopt(curl, CURLOPT_HTTPHEADER, nullptr);// 设置请求body: project_idstd::string json_data = nlohmann::json{{"project", project_id}}.dump();curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_data.c_str());CURLcode res;size_t total_size = -1;size_t content_length;bool is_total_size_calculated = false;std::string response_content;std::string response_headers;std::string range_header = "Range: " + std::to_string(start) + "-" + std::to_string(end);std::cout << "first range_header: " << range_header << std::endl << std::endl;int i = 1;struct curl_slist *headers = nullptr;try {do {std::cout << "第" << std::to_string(i)  <<"次循环:" << std::endl;i = i + 1;// 设置请求头, 包括Range和Content-Typeheaders = curl_slist_append(headers, "Content-Type: application/json");headers = curl_slist_append(headers, range_header.c_str());curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);// 设置响应头回调函数curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, HeaderCallback);curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response_headers);// 设置写入回调函数curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DownloadWriteCallback);curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_content);// 执行请求res = curl_easy_perform(curl);if (res != CURLE_OK) {std::cerr << "Download error: " << curl_easy_strerror(res) << std::endl;curl_slist_free_all(headers);curl_easy_cleanup(curl);//curl_global_cleanup();if (outfile.is_open()) {outfile.close(); // 关闭文件}return 1;} else {// 响应头存储在response_header中,解析response_header来获取需要的信息std::cout << "Response headers: "<< std::endl << response_headers << std::endl;// 解析响应头, Content-Range, Content-Lengthstd::istringstream header_stream(response_headers);std::string header_line;while (getline(header_stream, header_line)) {if (header_line.find("Content-Range:") == 0) {std::cout << "header_line: " << header_line << std::endl;if (!is_total_size_calculated ) {size_t pos = header_line.find('/');std::string total_size_str = header_line.substr(pos + 1);total_size = std::stoull(total_size_str);std::cout << "total_size: " << total_size << std::endl;is_total_size_calculated = true;}} else if (header_line.find("Content-Length:") == 0) {size_t pos = header_line.find(':');content_length = std::stoul(header_line.substr(pos + 2));std::cout << "content_length: " << content_length << std::endl;// 更新下一次请求的范围start += content_length;end = std::min(end + content_length, total_size);// 更新range_headerrange_header = "Range: bytes=" + std::to_string(start) + "-" + std::to_string(end);std::cout << "request range_header: " << range_header << std::endl;std::cout << "start: " << start << std::endl;std::cout << "end: " << end << std::endl << std::endl;}}// 将 responseContent 写入到文件outfile.write(response_content.c_str(), response_content.length());curl_slist_free_all(headers);headers = nullptr;response_headers.clear();response_content.clear();}} while (start < total_size);}catch (const std::bad_alloc& e) {std::cerr << "Memory allocation failed: " << e.what() << std::endl;if (headers) {curl_slist_free_all(headers);}curl_easy_cleanup(curl);if (outfile.is_open()) {outfile.close();}return 1;}curl_easy_cleanup(curl);if (outfile.is_open()) {outfile.close(); // 关闭文件}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java中函数式编程apply的用法实例?

在Java中&#xff0c;函数式编程的概念主要体现在Lambda表达式、函数接口以及Stream API的使用上。"apply"这个术语通常与函数式接口中的抽象方法关联&#xff0c;比如Function<T, R>接口中的apply方法。这个方法接收一个类型为T的输入参数&#xff0c;并返回一…

从零开始搭建创业公司全新技术栈解决方案

从零开始搭建创业公司全新技术栈解决方案 关于猫头虎 大家好&#xff0c;我是猫头虎&#xff0c;别名猫头虎博主&#xff0c;擅长的技术领域包括云原生、前端、后端、运维和AI。我的博客主要分享技术教程、bug解决思路、开发工具教程、前沿科技资讯、产品评测图文、产品使用体…

Ollma本地大模型沉浸式翻译【403报错解决】

最终效果 通过Chrome的 沉浸式翻译 插件&#xff0c;用OpenAI通用接口调用本地的Ollma上的模型&#xff0c;实现本地的大模型翻译文献。 官方文档指导的Ollama的配置&#xff1a;一定要配置环境变量&#xff0c;否则会出现【403报错】

GoogLeNet(InceptionV3)模型算法

GoogLeNet 团队在给出了一些通用的网络设计准则&#xff0c;以期望在不提高网络参数 量的前提下提升网络的表达能力&#xff1a; 避免特征图 (feature map) 表达瓶颈&#xff1a;从理论上讲&#xff0c;尺寸 (seize) 才包含了相关结构等重要因素&#xff0c;维度(channel) 仅仅…

torch.optim 之 Algorithms (Implementation: for-loop, foreach, fused)

torch.optim的官方文档 官方文档中文版 一、Implementation torch.optim的官方文档在介绍一些optimizer Algorithms时提及它们的implementation共有如下三个类别&#xff1a;for-loop, foreach (multi-tensor), and fused。 Chat-GPT对这三个implementation的解释是&#xf…

账号和权限的管理

文章目录 管理用户账号和组账号用户账号的分类超级用户普通用户程序用户 UID&#xff08;用户id)和(组账号)GIDUID用户识别号GID组标识号 用户账号文件添加用户账号设置/更改用户口令 管理用户账号和组账号 用户账号的分类 超级用户 root 用户是 Linux 操作系统中默认的超级…

React state 更新时机以及强制更新

设置 state 只会为下一次渲染变更 state 的值 一个 state 变量的值永远不会在一次渲染的内部发生变化 React 会使 state 的值始终"固定"在一次渲染的各个事件处理函数内部 React 会等到事件处理函数中的所有代码都运行完毕再处理 state 更新 用 flushSync 可以同步更…

PostgreSQL查询用户

在 PostgreSQL 中&#xff0c;可以通过查询系统表来确定当前用户是否是超级管理员&#xff08;超级用户&#xff09;。具体来说&#xff0c;可以使用 pg_roles 系统表&#xff0c;该表包含数据库中所有角色的信息。 以下是查询当前用户是否是超级用户的 SQL 语句&#xff1a; …

第N5周:调用Gensim库训练Word2Vec模型

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 目录 本周任务: 1.安装Gensim库 2.对原始语料分词 3.停用词 4.训练Woed2Vec模型 …

办展览如何盈利?论办展的商业模式

想要弄清楚办展览怎么赚钱这个问题&#xff0c;我可以来说说。 首先来说说展览收益的大头&#xff1a;门票收入。 这个其实是可以大致预测的。简单来说&#xff0c;就是用流量乘以到店率。 但别忘了&#xff0c;这背后得有合适的展览定位、方便的展览场地和合理的票价。 说…

封禁SSH登录的IP

在公网上开放SSHD服务时常被黑客扫描&#xff0c;可用以下方式封禁其IP&#xff1a; 1. 在/etc/hosts/deny中加&#xff1a;sshd: /etc/sshd.deny.hostguard echo "sshd: /etc/sshd.deny.hostguard" > /etc/hosts/deny 2. 然后将/var/log/secure中探测密码的远…

小林图解系统-三、操作系统结构

Linux 内核 vs Windows 内核 内核 作为应用连接硬件设备的桥梁&#xff0c;保证应用程序只需要关心与内核交互&#xff0c;不需要关心硬件的细节 内核具备四个基本能力&#xff1a; 管理进程、线程&#xff0c;决定哪个进程、线程使用CPU&#xff0c;也就是进程调度的能力&a…

Linux——ansible关于“文件操作”的模块

修改文件并将其复制到主机 一、确保受管主机上存在文件 使用 file 模块处理受管主机上的文件。其工作方式与 touch 命令类似&#xff0c;如果不存在则创建一个空文件&#xff0c;如果存在&#xff0c;则更新其修改时间。在本例中&#xff0c;除了处理文件之外&#xff0c;Ansi…

华为设备SSH远程访问配置实验简述

一、实验需求: 1、AR1模拟电脑SSH 访问AR2路由器。 二、实验步骤&#xff1a; 1、AR1和AR2接口配置IP&#xff0c;实现链路通信。 2、AR2配置AAA模式 配置用户及密码 配置用户访问级别 配置用户SSH 访问服务 AR2配置远程服务数量 配置用户远程访问模式为AAA 配置允许登录接入用…

【问题记录】Ubuntu提示: “E: 软件包 gcc 没有可安装候选“

Ubuntu提示: "E: 软件包 gcc 没有可安装候选" 一&#xff0c;问题现象二&#xff0c;问题原因&解决方法 一&#xff0c;问题现象 在虚拟机Ubuntu中进行安装gcc命令时报错&#xff1a;“E: 软件包 gcc 没有可安装候选”: 二&#xff0c;问题原因&解决方法 …

go-zero使用goctl生成mongodb的操作使用方法

目录 MongoDB简介 MongoDB的优势 对比mysql的操作 goctl的mongodb代码生成 如何使用 go-zero中mogodb使用 mongodb官方驱动使用 model模型的方式使用 其他资源 MongoDB简介 mongodb是一种高性能、开源、文档型的nosql数据库&#xff0c;被广泛应用于web应用、大数据以…

刷题——寻找峰值

寻找峰值_牛客题霸_牛客网 int findPeakElement(vector<int>& nums) {// write code hereint left 0, right nums.size() - 1;while(left < right){int mid (right - left) / 2 left;if(nums[mid] < nums[mid 1])left mid 1;elseright mid;}return lef…

Linux grep -r 遍历包括子文件内容和 jar包的类

grep -r 遍历包括子文件内容和 jar包的类 (base) [roottest002 24-06-20 13:38:47 flink-cdc-3.1.0 #] grep -r com.mysql.cj.jdbc.Driver * 匹配到二进制文件 lib/mysql-connector-java-8.0.27.jar (base) [roottest002 24-06-20 13:41:32 flink-cdc-3.1.0 #] vim 1 (base) [r…

性能测试-性能监控分析与调优(三)《实战》

性能监控 使用命令监控 cpu瓶颈分析 top命令 在进行性能测试时使用top命令&#xff0c;界面如下 上图可以看出 CPU 概况区&#xff1a; %Cpu(s): us&#xff08;用户进程占用CPU的百分比&#xff09;, 和 sy&#xff08;系统进程占用CPU的百分比&#xff09; 的数值很高…

FreeRTOS学习 -- 队列

一、队列简介 队列是为了任务与任务、任务与中断之间的通信而准备的&#xff0c;可以在任务与任务、任务与中断之间传递消息&#xff0c;队列中可以存储有限的、大小固定的数据项目。 任务与任务、任务与中断之间要交流的数据保存在队列中&#xff0c;叫做队列项目。 队列所能…