我的目标是编写一个程序,从ftp服务器下载一个包含所有文件和嵌套目录的目录。所以要做到这一点,我需要两个功能:1)列出目录中的所有文件/目录,2)下载特定文件。然后我可以递归遍历目录并在我的本地计算机上重新创建它。
因此,让我向您展示我正在使用的代码。
1)遗憾的是,对于目录遍历,我无法找到使用FtpWebRequest
可靠地完成目标的方法。因此,在我的实施中,我使用了两个ftp
命令ListDirectory
和ListDirectoryDetails
。第一个给我文件/目录名,第二个用于确定它是文件还是目录。 (遗憾的是,我无法找到一种可靠的方法来解析ListDirectoryDetails
返回的输出 - 每个人似乎都在使用他们自己的正则表达式,并且更糟糕的是,似乎不同的ftp服务器可能会报告它以不同的方式。)
//Example of strURI = "ftp://server123.domain.com/%2F/public_html"
FtpWebRequest ftpRequest1 = (FtpWebRequest)WebRequest.Create(strURI);
ftpRequest1.EnableSsl = true; //Use TLS!
ftpRequest1.Credentials = credentials;
ftpRequest1.KeepAlive = kbKeepAlive;
ftpRequest1.Timeout = knFtpTimeout;
ftpRequest1.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
using (FtpWebResponse response1 = (FtpWebResponse)ftpRequest1.GetResponse())
{
FtpWebRequest ftpRequest2 = (FtpWebRequest)WebRequest.Create(strURI);
ftpRequest2.EnableSsl = true; //Use TLS!
ftpRequest2.Credentials = credentials;
ftpRequest2.KeepAlive = kbKeepAlive;
ftpRequest2.Timeout = knFtpTimeout;
ftpRequest2.Method = WebRequestMethods.Ftp.ListDirectory;
using (StreamReader streamReader = new StreamReader(response1.GetResponseStream()))
{
List<string> arrFullDirList = new List<string>();
for (; ; )
{
string line = streamReader.ReadLine();
if (string.IsNullOrEmpty(line))
break;
arrFullDirList.Add(line);
}
using (FtpWebResponse response2 = (FtpWebResponse)ftpRequest2.GetResponse())
{
using (StreamReader streamReader2 = new StreamReader(response2.GetResponseStream()))
{
List<string> arrDirNamesList = new List<string>();
for (; ; )
{
string line = streamReader2.ReadLine();
if (string.IsNullOrEmpty(line))
break;
arrDirNamesList.Add(line);
}
//Then analyze two arrays
if (arrFullDirList.Count == arrDirNamesList.Count)
{
//If the first char in `arrFullDirList` item is 'd' then
//it's a directory. If it's '-' then it's a file...
}
}
response2.Close();
}
}
response1.Close();
}
2)对于文件下载,我使用以下代码:
//Query size of the file to be downloaded
//Example of strURI = "ftp://server123.domain.com/%2F/public_html/.htaccess"
FtpWebRequest requestSz = (FtpWebRequest)WebRequest.Create(strURI);
requestSz.EnableSsl = true; //Use TLS!
requestSz.Credentials = credentials;
requestSz.UseBinary = true;
requestSz.Method = WebRequestMethods.Ftp.GetFileSize;
requestSz.Timeout = knFtpTimeout;
using (FtpWebResponse responseSize = (FtpWebResponse)requestSz.GetResponse())
{
//File size
long uiFileSize = responseSize.ContentLength;
//Download file request
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(strURI);
request.EnableSsl = true; //Use TLS!
request.Credentials = credentials;
request.UseBinary = true;
request.Timeout = knFtpTimeout;
request.Method = WebRequestMethods.Ftp.DownloadFile;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
using (Stream ftpStream = response.GetResponseStream())
{
const int kBufferLength = 1024;
byte[] buffer = new byte[kBufferLength];
using (FileStream streamFile = File.Create(strLocalFilePath))
{
for (long uiProcessedSize = 0; ; )
{
int ncbRead = ftpStream.Read(buffer, 0, kBufferLength);
if (ncbRead == 0)
{
break;
}
streamFile.Write(buffer, 0, ncbRead);
uiProcessedSize += ncbRead;
double fProgress = (double)uiProcessedSize / (double)uiFileSize;
fProgress *= 100.0;
//Show download progress for a user ...
}
}
}
response.Close();
}
responseSize.Close();
}
最后我使用以下全局变量:
const bool kbKeepAlive = true;
const int knFtpTimeout = -1;
NetworkCredential credentials = new NetworkCredential("user_name", "password");
所以我使用这种方法尝试从(付费)共享网络托管服务器下载我的小网站文件。这种方式非常奇怪:
kbKeepAlive = true
,则下载速度会快得多,但是在某个可预测的时间点,FtpWebResponse
次调用之一会抛出此异常:基础连接已关闭:发生意外错误 收到。
kbKeepAlive = false
,则下载速度较慢,并且进度远远超过上述示例,但在某些时候它也会抛出同样的异常。这个过程是可重复的,感觉几乎就像是一个计数器,或者一些资源被耗尽,最终耗尽导致这个错误。
所以我做了一些搜索。这个例外有很多点击,但不幸的是,没有一个修复建议似乎适合我:
a)某人suggested increasing the timeout。好吧,我用我的knFtpTimeout = -1
完全删除了它,它没有任何区别。
b)然后有人suggested to enable network tracing并检查日志。所以我也这样做了。这是我在日志中得到的内容。我不确定它是否回答任何问题:
(日志本身非常大,但这里结束了。我编辑了实际的ftp服务器URL。)
[Subject]
CN=*.domain.com, OU=PositiveSSL Wildcard, OU=Domain Control Validated
Simple Name: *.domain.com
DNS Name: domain.com
[Issuer]
CN=COMODO RSA Domain Validation Secure Server CA, O=COMODO CA Limited, L=Salford, S=Greater Manchester, C=GB
Simple Name: COMODO RSA Domain Validation Secure Server CA
DNS Name: COMODO RSA Domain Validation Secure Server CA
[Serial Number]
00F61110ACEE4BF307C442973F3B842A2E
[Not Before]
2/3/2015 4:00:00 PM
[Not After]
3/7/2018 3:59:59 PM
[Thumbprint]
F429DCB7B8181F0236432890C3E10D99719AC698
[Signature Algorithm]
sha256RSA(1.2.840.113549.1.1.11)
[Public Key]
Algorithm: RSA
Length: 2048
Key Blob: 30 82 01 0a 02 82 01 01 00 d1 68 8c 67 75 9a f2 93 b1 25 95 2d 43 32 d6 83 18 07 09 ba 1a 2a d3 b3 b6 09 75 eb 92 05 9d 41 ab 38 ad a7 af 2a d1 5e f4 02 21 b0 d5 8b fe 54 17 da 90 2f c2 06 c7 6a 8b 57 fb 1b f9 4d 25 c4 9d b0 7c 31 94 19 ee 27 9c 81 ed b1 01 ba 7e 06 0f af d8 9a 81 94 25 ....
System.Net Information: 0 : [12064] SecureChannel#32368095 - Remote certificate was verified as valid by the user.
System.Net Information: 0 : [12064] ProcessAuthentication(Protocol=Tls, Cipher=Aes128 128 bit strength, Hash=Sha1 160 bit strength, Key Exchange=44550 256 bit strength).
System.Net Error: 0 : [12064] Decrypt failed with error 0X90317.
System.Net Information: 0 : [12064] FtpControlStream#11318800 - Received response [226-File successfully transferred
226 0.000 seconds (measured here), 0.58 Mbytes per second]
System.Net Information: 0 : [12064] FtpWebRequest#21940722::(Releasing FTP connection#11318800.)
System.Net Information: 0 : [12064] FtpWebRequest#41130254::.ctor(ftp://server123.domain.com///public_html/ctc)
System.Net Information: 0 : [12064] FtpWebRequest#41130254::GetResponse(Method=LIST.)
System.Net Information: 0 : [12064] Associating FtpWebRequest#41130254 with FtpControlStream#11318800
System.Net Information: 0 : [12064] FtpControlStream#11318800 - Sending command [CWD //public_html]
System.Net Information: 0 : [12064] FtpControlStream#11318800 - Received response [250 OK. Current directory is /public_html]
System.Net Information: 0 : [12064] FtpControlStream#11318800 - Sending command [PASV]
System.Net Information: 0 : [12064] FtpControlStream#11318800 - Received response [227 Entering Passive Mode (192,64,117,187,47,58)]
System.Net Information: 0 : [12064] FtpControlStream#11318800 - Sending command [LIST ctc]
System.Net Information: 0 : [12064] FtpWebRequest#41130254::(Releasing FTP connection#11318800.)
System.Net Error: 0 : [12064] Exception in FtpWebRequest#41130254::GetResponse - The underlying connection was closed: An unexpected error occurred on a receive..
at System.Net.FtpWebRequest.SyncRequestCallback(Object obj)
at System.Net.FtpWebRequest.RequestCallback(Object obj)
at System.Net.CommandStream.Dispose(Boolean disposing)
at System.IO.Stream.Close()
at System.IO.Stream.Dispose()
at System.Net.ConnectionPool.Destroy(PooledStream pooledStream)
at System.Net.ConnectionPool.PutConnection(PooledStream pooledStream, Object owningObject, Int32 creationTimeout, Boolean canReuse)
at System.Net.FtpWebRequest.FinishRequestStage(RequestStage stage)
at System.Net.FtpWebRequest.GetResponse()
System.Net Information: 0 : [12064] FtpControlStream#33675143 - Received response [226-Options: -a
226 133 matches total]
System.Net Information: 0 : [12064] FtpWebRequest#21454193::(Releasing FTP connection#33675143.)
所以我知道我做错了什么?