最近在使用curl时遇到一个问题,老出现下面的错误提示:
< 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
在网上找了很长时间,发现真正有价值的博客非常少,只有两个:
http://blog.dou.li/curl-9-server-denied-you-to-change-to-the-given-directory.html和
http://blog.csdn.net/langeldep/article/details/6166218,不过少比没有好,第二个博客的方法确实能解决问题,所以,我将解决该问题的方法稍作整理。
解决该问题的方法有三种:
第一种是上面链接1,通过严格地设置文件的路径,但是我测试发现不好使。
第二种方法是上面链接2,通过修改代码实现。测试发现确实能解决问题。
第三种方法,没必要修改源码,这不是BUG,只要添加下面的语句设置好参数就行了,不过这种方法我没测试,不知道好不好用。
curl_easy_setopt(curl, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD);
第二种方法的详细解释和操作过程为:
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
修改方法是 修改 curl库目录下面 lib文件夹下面的 ftp.c 文件,在函数 static CURLcode ftp_parse_url_path(struct connectdata *conn) 中 添加如下代码
if (ftpc->dirdepth == 0)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>ftpc->dirs[0] = strdup("/");
<span style="white-space:pre"> </span>ftpc->dirdepth++;
<span style="white-space:pre"> </span>}
意思就是 让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;
<span style="color:#ff0000;"> if (ftpc->dirdepth == 0)
{
ftpc->dirs[0] = strdup("/");
ftpc->dirdepth++;
}</span>
/* 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 */
然后按照前一篇博客http://blog.csdn.net/xueyushenzhou/article/details/51702672的说明,重新编译出共享库,再将原来的都替换掉就行啦。