(四)使用Libcurl上载文件,解决无信号中断,上载中掉电恢复后断点续传有关问题的源代码
(四)使用Libcurl下载文件,解决无信号中断,下载中掉电恢复后断点续传问题的源代码
源代码附上:
//全局变量 bool resumeDownload = false; //是否需要下载的标记位 long downloadFileLenth = 0; //需要下载的总大小, 远程文件的大小
/* 得到本地文件大小的函数, 若不是续传则返回0, 否则返回指定路径地址的文件大小 */ long getLocalFileLenth(const char* localPath){ if (!resumeDownload){ return 0; } return fs_open(localPath).fs_size(); }
/* 得到远程文件的大小, 要下载的文件大小 */ long getDownloadFileLenth(const char *url){ long downloadFileLenth = 0; CURL *handle = curl_easy_init(); curl_easy_setopt(handle, CURLOPT_URL, url); curl_easy_setopt(handle, CURLOPT_HEADER, 1); //只需要header头 curl_easy_setopt(handle, CURLOPT_NOBODY, 1); //不需要body if (curl_easy_perform(handle) == CURLE_OK) { curl_easy_getinfo(handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &downloadFileLenth); } else { downloadFileLenth = -1; } return downloadFileLenth; }
/* scomoDownload回调的计算进度条的函数 */ void getProgressValue(const char* localSize, double dt, double dn, double ult, double uln){ double showTotal, showNow; showTotal = downloadFileLenth; int localNow = atoi (localSize.c_str()); showNow = localNow + dn; showProgressBar(showTotal, showNow); }
/* 直接进行下载的函数 */ public CurlCode scomoDownload(long timeout) { long localFileLenth = getLocalFileLenth(); const char *localFileLenthStr; sprint(localFileLenthStr, %ld, localFileLenth); curl_easy_setopt(handle, CURLOPT_URL, mUrl); curl_easy_setopt(handle, CURLOPT_HEADER, 0); curl_easy_setopt(handle, CURLOPT_TIMEOUT, timeout); curl_easy_setopt(handle, CURLOPT_CONNECTIONTIMEOUT, 0); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, &writeDataCallback); curl_easy_setopt(handle, CURLOPT_WRITEDATA, this); curl_easy_setopt(handle, CURLOPT_RESUME_FROM_LARGE, localFileLenth); curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0); curl_easy_setopt(handle, CURLOPT_ PROGRESSFUNCTION, getProgressValue); curl_easy_setopt(handle, CURLOPT_PROGRESSDATA, localFileLenthStr); if (curl_easy_perform) { resumeDownload = true; return DS_FAILED; } else { resumeDownload = false; return DS_FINISHED; } }
/* downloadControl函数用来控制整个下载过程的节奏, 控制下载的次数, 每次等待的时间等 */ public void downloadControler(){ downloadFileLenth = getDownloadFileLenth(); //下载前得到要下载的文件大小赋值给全局变量 int times = 605; //600次*50ms=5分钟, 以此确保5分钟内的重试次数, 而5次是正常下载的中断次数, 意思即是5次内能正常完成下载. int count = 0; int timeout = 30; DSTATUS dstatus = DS_FAILED; while (count++ < times){ status = scomoDownload(timeout); if (dstatus == DS_FINISHED){ break; } Thread.sleep(500); //每次下载中间间隔500毫秒 } resumeDownload = false; //不管下载成功或失败, 完成while循环后将标志回位 if (dstaus == DS_FINISHED) { updateApp(); //执行软件安装的操作… } SAFE_DELETE(localFile); //流程最后要确保本地文件删除 }
resumeDownload是一个非常重要的标记位, 主要用来标识是否需要续传下载, 在初始化时为false, 在下载完成后也应回位成false, 下载过程中若因时间中断未下载完成也为false.
处理下载中掉电后续传也需要这个标记位, 在程序启动时, 进行检测, 若上次没下载完, 修改标志位为true, 然后调用下载入口函数downloadController:
if (scomo_status == 30){
resumeDownload = true;
downloadController();
}
若下载环境正常, 1个小时内可以完成的下载可以直接使用此方案来下载, 不用修改控制, 但若是超过1小时的下载, 需要将本方案进行改进. 基本上就是将605那里分开判断600+x, 其中600为每次断网后应重试的次数, x为正常下载应该进行的计数, 分别计算即可.