news 2026/10/15 20:15:22

curl使用读回调来分块上报文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
curl使用读回调来分块上报文件

之前的文章《curl编程实例-上传文件》,介绍过如何使用curl编程来上传文件,是通过指定文件路径的方式来上传,关键的特征如下:

constchar*file_path="./test.txt";// 要上传的本地文件// 打开待上传的文件(二进制模式)FILE*fp=fopen(file_path,"rb");curl_easy_setopt(curl,CURLOPT_READDATA,fp);curl_easy_perform(curl);

在有些情况下,可能需要对文件进行分段上传,这种情况,可以使用curl的读回调机制,通过多次的回调函数的调用,每次上传部分内容,最终上传整个文件。

1 读回调的编写

1.1 分段上传参数

需要先定义一个参数,用来在回调函数中,记录上传的数据信息:

  • const char *filename;:要上传的文件名
  • size_t totalSize;:要上传的总大小
  • size_t uploadedSize;:目前已上传的大小
typedefstruct{constchar*filename;size_ttotalSize;size_tuploadedSize;}UploadInfo_t;

1.2 读回调的定义

curl的读回调,会自动给出每次需要上传的数据大小,回调中需要做的,就是根据回调参数中指定的需要上传的大小,将文件的分段内容,写入指定的缓冲区,然后将已上传的大小,记录到前面定义的UploadInfo_t结构中。

size_tfile_read_cb(char*buf,size_tsize,size_tn,void*uploadInfo){if(!uploadInfo){return0;}UploadInfo_t*info=(UploadInfo_t*)uploadInfo;size_tbufferSize=size*n;// 此次需要上传的大小curl_off_tremaining=info->totalSize-info->uploadedSize;if(remaining<=0){return0;// 已读完,结束传输}if(bufferSize>(size_t)remaining){bufferSize=(size_t)remaining;}size_tbytesRead=custom_read_file(info->filename,info->uploadedSize,bufferSize,buf);if(bytesRead==0){return0;}info->uploadedSize+=bytesRead;floatprogress=(float)info->uploadedSize/info->totalSize*100;printf("[%s] read:%zu bytes, progress:%.1f%%\n",__func__,bytesRead,progress);returnbytesRead;}

1.3 分段读取举例

分段读取的实现形式有很多,比如通过自定义的接口,从自定义的内存缓冲区,或其它自定义的方式,进行读取。

这里只是演示分段读取的过程,就还以fopen读文件的方式举例,再通过fseeko进行偏移,从而实现从文件的指定位置读取指定长度的内容。

size_tcustom_read_file(constchar*file_path,size_toffset,size_tread_len,char*buffer){size_tactual_read=0;// 这里只是使用fopen举例,实际可以是任何形式的文件读取FILE*fp=fopen(file_path,"rb");if(!fp){printf("fopen:%s err\n",file_path);return0;}// 定位到指定偏移量:SEEK_SET 表示从文件开头计算偏移intseek_ret=fseeko(fp,offset,SEEK_SET);if(seek_ret!=0){printf("fseeko failed: offset=%lld\n",(longlong)offset);gotoEND;}// 读取指定长度的数据到缓冲区actual_read=fread(buffer,1,read_len,fp);if(actual_read!=read_len){// 读取不完整:可能是到文件末尾,或读取错误if(feof(fp)){printf("Warning: only read %zu bytes(expect:%zu)\n",actual_read,read_len);}elseif(ferror(fp)){printf("fread failed");}}END:fclose(fp);returnactual_read;}

2 完整代码

完整代码如下,是在之前那篇《curl编程实例-上传文件》的基础上进行修改的。

// gcc file_upload2.c -o file_upload2 -lcurl#include<stdio.h>#include<stdlib.h>#include<sys/stat.h>#include<curl/curl.h>#include<string.h>#include<libgen.h>// 用于提取原始文件名// 进度回调函数staticintupload_progress(void*p,curl_off_tdltotal,curl_off_tdlnow,curl_off_tultotal,curl_off_tulnow){if(ultotal>0){// 计算进度百分比,限制最大值为100%(实际的上传数据包含了HTTP头部等数据)floatprogress=(ulnow*100.0)/ultotal;if(progress>100.0)progress=100.0;printf("progress: %lld/%lld (%.2f%%)\n",(longlong)ulnow,(longlong)ultotal,progress);}return0;}// 提取文件路径中的原始文件名(兼容绝对/相对路径)char*get_original_filename(constchar*file_path){if(!file_path){returnstrdup("unknown_file.dat");}char*path_copy=strdup(file_path);char*filename=basename(path_copy);char*result=strdup(filename);free(path_copy);returnresult;}// 获取文件实际大小(字节)curl_off_tget_file_size(constchar*file_path){if(!file_path){return-1;}structstatst;if(stat(file_path,&st)==-1){printf("stat %s failed\n",file_path);return-1;}return(curl_off_t)st.st_size;}typedefstruct{constchar*filename;size_ttotalSize;size_tuploadedSize;}UploadInfo_t;size_tcustom_read_file(constchar*file_path,size_toffset,size_tread_len,char*buffer){size_tactual_read=0;// 这里只是使用fopen举例,实际可以是任何形式的文件读取FILE*fp=fopen(file_path,"rb");if(!fp){printf("fopen:%s err\n",file_path);return0;}// 定位到指定偏移量:SEEK_SET 表示从文件开头计算偏移intseek_ret=fseeko(fp,offset,SEEK_SET);if(seek_ret!=0){printf("fseeko failed: offset=%lld\n",(longlong)offset);gotoEND;}// 读取指定长度的数据到缓冲区actual_read=fread(buffer,1,read_len,fp);if(actual_read!=read_len){// 读取不完整:可能是到文件末尾,或读取错误if(feof(fp)){printf("Warning: only read %zu bytes(expect:%zu)\n",actual_read,read_len);}elseif(ferror(fp)){printf("fread failed");}}END:fclose(fp);returnactual_read;}size_tfile_read_cb(char*buf,size_tsize,size_tn,void*uploadInfo){if(!uploadInfo){return0;}UploadInfo_t*info=(UploadInfo_t*)uploadInfo;size_tbufferSize=size*n;// 此次需要上传的大小curl_off_tremaining=info->totalSize-info->uploadedSize;if(remaining<=0){return0;// 已读完,结束传输}if(bufferSize>(size_t)remaining){bufferSize=(size_t)remaining;}size_tbytesRead=custom_read_file(info->filename,info->uploadedSize,bufferSize,buf);if(bytesRead==0){return0;}info->uploadedSize+=bytesRead;floatprogress=(float)info->uploadedSize/info->totalSize*100;printf("[%s] read:%zu bytes, progress:%.1f%%\n",__func__,bytesRead,progress);returnbytesRead;}intmain(intargc,char*argv[]){CURL*curl=NULL;CURLcode res;FILE*fp=NULL;constchar*upload_url="http://192.168.5.104:8080/upload";// 文件服务器的地址constchar*file_path="./test.jpg";// 要上传的本地文件structcurl_slist*headers=NULL;// 自定义请求头// 文件上传的信息UploadInfo_t uploadInfo={0};uploadInfo.filename=file_path;// 获取文件大小size_tfile_size=get_file_size(file_path);if(file_size<0){printf("%s file_size:%zu err\n",file_path,file_size);gotocleanup;}printf("%s file_size: %zu\n",file_path,file_size);uploadInfo.totalSize=file_size;// 初始化libcurlcurl_global_init(CURL_GLOBAL_ALL);curl=curl_easy_init();if(!curl){printf("curl_easy_init, err\n");gotocleanup;}// 构建自定义请求头(传递原始文件名)char*original_filename=get_original_filename(file_path);charheader_buf[256];snprintf(header_buf,sizeof(header_buf),"X-File-Name: %s",original_filename);headers=curl_slist_append(headers,header_buf);// 禁用Expect头,解决POST上传阻塞问题headers=curl_slist_append(headers,"Expect:");// 设置上传URLcurl_easy_setopt(curl,CURLOPT_URL,upload_url);// 设置自定义请求头curl_easy_setopt(curl,CURLOPT_HTTPHEADER,headers);// 设置文件上传curl_easy_setopt(curl,CURLOPT_UPLOAD,1L);curl_easy_setopt(curl,CURLOPT_INFILESIZE_LARGE,(curl_off_t)file_size);curl_easy_setopt(curl,CURLOPT_READFUNCTION,file_read_cb);curl_easy_setopt(curl,CURLOPT_READDATA,&uploadInfo);// 执行上传printf("start uoload file: %s\n",file_path);res=curl_easy_perform(curl);if(res!=CURLE_OK){printf("upload fail: %s\n",curl_easy_strerror(res));}else{printf("upload success\n");}// 资源清理(统一出口)cleanup:if(original_filename)free(original_filename);if(headers)curl_slist_free_all(headers);if(curl)curl_easy_cleanup(curl);curl_global_cleanup();returnres==CURLE_OK?0:1;}

3 运行结果

测试环境:

  • 在windows电脑上启动一个文件服务器,可参考之前的文章《curl编程实例-上传文件》
  • 在ubuntu虚拟机上启动文件上传程序

可以看到,文件通过回调的方式,多次分段上传,最终的windows电脑的文件服务器的指定目录,可以看到上传的文件

4 总结

本篇介绍了如何使用curl的C语言编程,读回调的方式,实现文件的分段上传,并通过代码实例,验证上传的结果。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 11:29:05

AD导出Gerber文件教程:通俗解释各选项含义

AD导出Gerber文件实战指南&#xff1a;彻底搞懂每一项设置的真正含义你有没有遇到过这种情况——PCB打样回来&#xff0c;发现焊盘没开窗、丝印错位&#xff0c;甚至整块板子短路&#xff1f;别急着怀疑工厂水平&#xff0c;问题很可能出在你自己导出的Gerber文件上。在Altium …

作者头像 李华
网站建设 2026/10/15 17:17:38

零代码实现文档OCR:MinerU开箱即用体验

零代码实现文档OCR&#xff1a;MinerU开箱即用体验 1. 背景与需求痛点 1.1 文档数字化的现实挑战 在企业知识管理、学术研究和金融分析等场景中&#xff0c;大量非结构化文档&#xff08;如PDF扫描件、财报截图、论文图像&#xff09;需要转化为可编辑、可检索的文本数据。传…

作者头像 李华
网站建设 2026/10/11 15:44:55

DeepSeek-R1-Distill-Qwen-1.5B开源贡献:社区协作开发指南

DeepSeek-R1-Distill-Qwen-1.5B开源贡献&#xff1a;社区协作开发指南 1. 引言 1.1 项目背景与技术动机 随着大语言模型在推理能力、代码生成和数学解题等复杂任务中的需求不断增长&#xff0c;如何高效提升中小规模模型的智能表现成为社区关注的核心问题。DeepSeek-R1-Dist…

作者头像 李华
网站建设 2026/10/13 12:27:03

Qwen2.5-0.5B部署教程:Mac M系列芯片适配方案

Qwen2.5-0.5B部署教程&#xff1a;Mac M系列芯片适配方案 1. 引言 1.1 学习目标 本文旨在为开发者提供一份完整的 Qwen2.5-0.5B-Instruct 模型在 Mac M 系列芯片上的本地部署指南。通过本教程&#xff0c;您将掌握&#xff1a; 如何在 Apple Silicon 架构&#xff08;M1/M2…

作者头像 李华
网站建设 2026/10/15 9:41:10

HY-MT1.8B推理延迟高?vllm优化部署案例提速300%

HY-MT1.8B推理延迟高&#xff1f;vllm优化部署案例提速300% 1. 背景与问题提出 在多语言业务场景中&#xff0c;实时翻译服务的性能直接影响用户体验。混元翻译模型&#xff08;HY-MT&#xff09;系列自开源以来&#xff0c;凭借其在小参数量下实现高质量翻译的能力&#xff…

作者头像 李华
网站建设 2026/10/15 9:56:31

WarcraftHelper全面解析:让魔兽争霸III在现代系统重获新生

WarcraftHelper全面解析&#xff1a;让魔兽争霸III在现代系统重获新生 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为魔兽争霸III在现代电脑上…

作者头像 李华