CURL 库在使用FTP传输文件时, 正常情况下, ftp的server端都会默认进入根目录 /, 但是当ftp的server端设置了一个子目录,
比如 ftp的server设置了 /data/movies 为第一次默认进入的目录, 则curl的FTP将会无法传输文件, 错误的原因是 :
ftp的下载地址为 ftp://root:[email protected]/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 _WIN32 struct _stat file_info; #else struct stat file_info; #endif int 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,就代表启用PASV curl_easy_setopt(curlhandle, CURLOPT_NOPROGRESS, 1L); #ifdef _DEBUG curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1L); #endif result = 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); else SNPRINTF (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 command CWD requires a parameter and a non-existant parameter a) doesn't work 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 */