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 目录, 目录肯定是不存在的,
所以出错。
具体的错误现象如下:
* 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
下面的代码是我使用的代码
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) 中 添加如下代码
意思就是 让ftp客户端从 根目录开始一级一级的进入到文件所在的目录。
添加的位置如下 :
/* 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 */