news 2026/4/15 18:26:44

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/3/27 0:30:24

GetQzonehistory:QQ空间历史说说完整备份解决方案

GetQzonehistory&#xff1a;QQ空间历史说说完整备份解决方案 【免费下载链接】GetQzonehistory 获取QQ空间发布的历史说说 项目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 在数字时代&#xff0c;QQ空间承载着我们多年来的情感记忆和生活点滴。Get…

作者头像 李华
网站建设 2026/4/15 14:42:57

iOS个性化定制新选择:Cowabunga Lite全方位实战指南

iOS个性化定制新选择&#xff1a;Cowabunga Lite全方位实战指南 【免费下载链接】CowabungaLite iOS 15 Customization Toolbox 项目地址: https://gitcode.com/gh_mirrors/co/CowabungaLite 厌倦了千篇一律的iOS界面&#xff1f;想要在不越狱的情况下实现个性化定制&am…

作者头像 李华
网站建设 2026/4/12 18:52:40

ComfyUI-Manager极速下载实战指南:让你的模型下载飞起来

ComfyUI-Manager极速下载实战指南&#xff1a;让你的模型下载飞起来 【免费下载链接】ComfyUI-Manager 项目地址: https://gitcode.com/gh_mirrors/co/ComfyUI-Manager 还在为等待模型下载而浪费宝贵时间吗&#xff1f;想象一下&#xff0c;当你灵感迸发准备创作时&…

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

Hanime1观影助手完整使用手册:告别广告困扰的终极解决方案

Hanime1观影助手完整使用手册&#xff1a;告别广告困扰的终极解决方案 【免费下载链接】Hanime1Plugin Android插件(https://hanime1.me) (NSFW) 项目地址: https://gitcode.com/gh_mirrors/ha/Hanime1Plugin 你是否曾经在观看精彩动画时被突如其来的广告打断&#xff1…

作者头像 李华
网站建设 2026/4/9 22:48:41

AIVideo影视级输出:4K视频生成的可能性探索

AIVideo影视级输出&#xff1a;4K视频生成的可能性探索 1. 引言&#xff1a;AI长视频创作的新范式 随着生成式AI技术的快速发展&#xff0c;AI在视频内容创作领域的应用正从“片段生成”迈向“全流程自动化生产”。AIVideo作为一站式AI长视频工具&#xff0c;标志着AI视频生成…

作者头像 李华