huankfy

明月出天山,苍茫云海间

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  12 随笔 :: 16 文章 :: 24 评论 :: 0 引用

公告

      项目中有个ftp组件,在下载中文命名的文件时,总抛出错误“Access is denied, cannot be found the file specified”,英文命名的文件却能够正常下载。首先想到的是编码问题,将文件名做了一次UrlEncode,仍是同样的错误。后来查找代码,发现使用了非标准的ftp command。

  先看一下下载代码部分:

 1 public void OpenDownload(string remoteFileName, string localFileName, bool resume){
 2          ***********code etc**************
 3             SetBinaryMode(true);
 4             OpenDataSocket();
 5 
 6             bytes_total = 0;
 7 
 8             _fileSize = GetFileSize(remoteFileName);  
 9 
10             Byte[] cmd = Encoding.UTF8.GetBytes(("RETR ").ToCharArray());
11             byte[] bytes = Encoding.UTF8.GetBytes(remoteFileName);
12             byte[] content = Encoding.Convert(Encoding.UTF8, Encoding.Default, bytes);
13             int totalLength = cmd.Length + content.Length;
14             byte[] re = new byte[totalLength + 2];
15             for (int i = 0; i < cmd.Length; i++)
16             {
17                 re[i] = cmd[i];
18             }
19             for (int i = 0; i < content.Length; i++)
20             {
21                 re[cmd.Length + i] = content[i];
22             }
23             re[totalLength] = 13;
24             re[totalLength + 1= 10;
25 
26             main_sock.Send(re, re.Length, 0);
27 
28             ReadResponse();
29          ***********code etc**************
30 }

 

  抛出问题的语句做了下标注。此处是获取远程服务器上文件大小。具体代码:

获取文件大小
 1 /// <summary>
 2 /// 获取文件大小
 3 /// </summary>
 4 /// <param name="filename">文件名</param>
 5 /// <returns>文件大小</returns>
 6 public long GetFileSize(string fileName){
 7     Connect();
 8     SendCommand("SIZE " + fileName);
 9     ReadResponse();
10 
11     if (response != 213throw new ServerException(responseStr);
12 
13         return Int64.Parse(responseStr.Substring(4), CultureInfo.CurrentCulture);
14 }

  其中,SendCommand函数为

1 private void SendCommand(string command){
2     Byte[] cmd = Encoding.ASCII.GetBytes((command + "\r\n").ToCharArray());
3     main_sock.Send(cmd, cmd.Length, 0);
4 }

  socked发出ftp的size命令后,得到的响应始终不会是213,抛出的异常就是找不到指定的文件,不论文件名是否做过unicode编码。

  在SendCommand函数中,可以看到,命令被作为ascii编码的串进行字节转换。换成Unicode?服务器会抛出超时错误:“Timed out waiting on server to respond”。UTF8?服务器则会抛出与ASCII编码一样的错误。

  后来查阅了相关的文档,发现ftp的size命令为非标准的command。在Ascii编码体系中,支持size command来获取文件大小。一些Unix的ftp服务中,已经不支持非标准的命令。size存在与ascii编码体系中,所以,如果是非ascii编码的文件名,通过size是获取文件大小,服务器就会返回550错误:"Access is denied, cannot be found the file specified"。

     问题总算找到了,中文等不在ascii码范围,发送size命令,ftp服务器(支持非标准ftp command,否则ftp server会返回500错误:不能识别的命令)会将文件名当成ascii码解析查找,中文文件名自然是查找不到,进行urlecode之后,经过一系列的字节转换,发送到ftp server之后,相当于文件名做了变更,自然找不到文件。

  接下来就是修改问题了,文件大小的计算放在了文件被下载到本地后,而没有放在下载前,同时,编码统一为UFT8。至此解决完成。

 

 

posted on 2010-06-13 16:27 Yanbo.Hu 阅读(...) 评论(...) 编辑 收藏