|
CURL 库在使用FTP传输文件时, 正常情况下, ftp的server端都会默认进入根目录 /, 但是当ftp的server端设置了一个子目录,
比如 ftp的server设置了 /data/movies 为第一次默认进入的目录, 则curl的FTP将会无法传输文件, 错误的原因是 :
ftp的下载地址为 ftp://root:123456@192.168.2.223/data/movies/vbr.ts
而第一次进入的目录为 /data/movies , 而curl库的FTP解析ftp地址得出 要进入 data 和movies 目录后才能找到下载的文件,
就会去执行 CWD data 和 CWD movies , 而在 /data/movies 这个目录去进入 data 目录, 目录肯定是不存在的,
所以出错。
具体的错误现象如下:
* About to connect() to 192.168.2.223 port 21 (#0)* Trying 192.168.2.223... Leave Lock = Resume Download File* connected* Connected to 192.168.2.223 (192.168.2.223) port 21 (#0)< 220 (vsFTPd 2.0.5)> USER root< 331 Please specify the password.> PASS 123456< 230 Login successful.> PWD< 257 "/data/movies"* Entry path is '/data/movies'> CWD data< 550 Failed to change directory.* Server denied you to change to the given directory* Connection #0 to host 192.168.2.223 left intact* Access denied to remote resource> QUIT< 221 Goodbye.* Closing connection #0
下面的代码是我使用的代码
void dispatch_threadpool_to_me(void *arg){MissionInfo* pMission = (MissionInfo*)arg;uint64 filelen = 0;int tries = 0;CURL *curlhandle;RE_Download:FILE *fp;curl_off_t local_file_len = -1 ;uint64 filesize =0 ;CURLcode result = CURLE_GOT_NOTHING;#ifdef _WIN32struct _stat file_info;#elsestruct stat file_info;#endifint use_resume = 0;/* 得到本地文件大小 */if(stat(pMission->localPath, &file_info) == 0){int flen = file_info.st_size;if (flen%DOWNLOAD_FILE_SEGMENT == 0){//assert (flen > 0);local_file_len = flen - DOWNLOAD_FILE_SEGMENT;}else{local_file_len = flen - flen%DOWNLOAD_FILE_SEGMENT - DOWNLOAD_FILE_SEGMENT;}local_file_len = (local_file_len > 0) ? local_file_len : 0;assert (local_file_len%DOWNLOAD_FILE_SEGMENT == 0);use_resume = 1;}if (pMission->missionType == MISSION_TYPE_REMOVEFILE){//删除任务if (use_resume == 0){writelog(LOG_ERROR, "remove file is not exists, path=%s/n", pMission->localPath);}unlink(pMission->localPath);(*pMission->downcbfunc)(pMission->missionID, pMission->missionType, pMission->epgID, 0, "removeFileSuccess", pMission->localPath, 0);return;}curlhandle = curl_easy_init();if(curlhandle == NULL)return ;//采用追加方式打开文件,便于实现文件断点续传工作fp = fopen(pMission->localPath, "ab+");//ftp服务器可能无法断点续传,所以不能追加写//fp = fopen(pMission->localPath, "w");if (fp == NULL){curl_easy_cleanup(curlhandle);writelog(LOG_ERROR, "Download fopen file Error , file=%s/n", pMission->localPath);return ;}curl_easy_setopt(curlhandle, CURLOPT_URL, pMission->remotePath);curl_easy_setopt(curlhandle, CURLOPT_CONNECTTIMEOUT, 60); // 设置连接超时,单位秒curl_easy_setopt(curlhandle, CURLOPT_TIMEOUT, 1800);//设置http 头部处理函数curl_easy_setopt(curlhandle, CURLOPT_HEADERFUNCTION, GetContentLengthFunc);curl_easy_setopt(curlhandle, CURLOPT_HEADERDATA, &filesize);// 设置文件续传的位置给libcurl//use_resume = 0; //服务器不支持断点续传的时候无法续传,所以强制从头开始下载文件curl_easy_setopt(curlhandle, CURLOPT_RESUME_FROM_LARGE, use_resume?local_file_len:0);curl_easy_setopt(curlhandle, CURLOPT_WRITEDATA, fp);curl_easy_setopt(curlhandle, CURLOPT_WRITEFUNCTION, wirtefunc);curl_easy_setopt(curlhandle, CURLOPT_FTP_USE_EPSV, 0); //EPSV设置为0,就代表启用PASVcurl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1L);#ifdef _DEBUGcurl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L);#endifresult = curl_easy_perform(curlhandle);curl_easy_cleanup(curlhandle);if (result == CURLE_OK){fseek(fp, 0, SEEK_END);filelen = ftell(fp);fclose(fp);writelog(LOG_ERROR, "Success Download File local = %s, remote = %s/n", pMission->localPath, pMission->remotePath);char buffer[512]={0};if (CompareMD5 (pMission->localPath)){const char* p = g_Config.pukkaIndex;if (*p == '.' || *p == '/')SNPRINTF (buffer, sizeof(buffer)-1, "%s %s", g_Config.pukkaIndex, pMission->localPath);elseSNPRINTF (buffer, sizeof(buffer)-1, "./%s %s", g_Config.pukkaIndex, pMission->localPath);system (buffer);(*pMission->downcbfunc)(pMission->missionID, pMission->missionType, pMission->epgID, 0, "ok", pMission->localPath, filelen);return ;}else{}}else if (result == CURLE_OPERATION_TIMEOUTED){fclose(fp);writelog(LOG_ERROR, "down operation timeout/n");goto RE_Download;}else if (result == CURLE_COULDNT_CONNECT){writelog(LOG_ERROR, "Can not Connect FTP File local = %s, remote = %s/n", pMission->localPath, pMission->remotePath);(*pMission->downcbfunc)(pMission->missionID, pMission->missionType, pMission->epgID, -1, curl_easy_strerror(result), pMission->localPath, filelen);return ;}else{fclose(fp);tries ++;if (tries < MAX_TRY_DOWNLOAD_COUNT){//writelog(LOG_ERROR, "Download File Try = %d /n localdir = %s /n remotedir = %s /n err=%s/n",//tries, pMission->localPath, pMission->remotePath, curl_easy_strerror(result));sleep(MAX_SLEEP_TIME);if (result == CURLE_BAD_DOWNLOAD_RESUME){unlink(pMission->localPath);//不能断点续传下载,则删除本地已经下载的部分文件}goto RE_Download;}else if (tries == MAX_TRY_DOWNLOAD_COUNT){//万一ftp的服务器不支持断点续传,所以重新把文件删除, 在下载一次tries++;unlink(pMission->localPath);goto RE_Download;}else{writelog(LOG_ERROR, "Fail Download File local = %s, remote = %s/n", pMission->localPath, pMission->remotePath);(*pMission->downcbfunc)(pMission->missionID, pMission->missionType, pMission->epgID, -1, curl_easy_strerror(result), pMission->localPath, filelen);return ;}}}
修改方法是 修改 curl库目录下面 lib文件夹下面的 ftp.c文件
在函数 static CURLcode ftp_parse_url_path(struct connectdata *conn) 中 添加如下代码
if (ftpc->dirdepth == 0){ftpc->dirs[0] = strdup("/");ftpc->dirdepth++;}
意思就是 让ftp客户端从 根目录开始一级一级的进入到文件所在的目录。
添加的位置如下 :
default: /* allow pretty much anything */case FTPFILE_MULTICWD:ftpc->dirdepth = 0;ftpc->diralloc = 5; /* default dir depth to allocate */ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));if(!ftpc->dirs)return CURLE_OUT_OF_MEMORY;/* we have a special case for listing the root dir only */if(strequal(path_to_use, "/")) {cur_pos++; /* make it point to the zero byte */ftpc->dirs[0] = strdup("/");ftpc->dirdepth++;}else {/* parse the URL path into separate path components */while((slash_pos = strchr(cur_pos, '/')) != NULL) {/* 1 or 0 pointer offset to indicate absolute directory */ssize_t absolute_dir = ((cur_pos - data->state.path > 0) && (ftpc->dirdepth == 0))?1:0;if (ftpc->dirdepth == 0){ftpc->dirs[0] = strdup("/");ftpc->dirdepth++;}/* seek out the next path component */if(slash_pos-cur_pos) {/* we skip empty path components, like "x//y" since the FTP commandCWD requires a parameter and a non-existant parameter a) doesn'twork on many servers and b) has no effect on the others. */int len = (int)(slash_pos - cur_pos + absolute_dir);ftpc->dirs[ftpc->dirdepth] = curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);if(!ftpc->dirs[ftpc->dirdepth]){ /* run out of memory ... */failf(data, "no memory");freedirs(ftpc);return CURLE_OUT_OF_MEMORY;}if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {free(ftpc->dirs[ftpc->dirdepth]);freedirs(ftpc);return CURLE_URL_MALFORMAT;}}else {cur_pos = slash_pos + 1; /* jump to the rest of the string */continue;}cur_pos = slash_pos + 1; /* jump to the rest of the string */if(++ftpc->dirdepth >= ftpc->diralloc) {/* enlarge array */char *bigger;ftpc->diralloc *= 2; /* double the size each time */bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));if(!bigger) {freedirs(ftpc);return CURLE_OUT_OF_MEMORY;}ftpc->dirs = (char **)bigger;}}}filename = cur_pos; /* the rest is the file name */break;} /* switch */ |
|