我正在尝试使用方法FtpWebRequest
使用WebRequestMethods.Ftp.DownloadFile
从FTP下载文件的简单方法。问题是我不想显示下载的进度,因此需要知道前面的文件大小才能计算转移的百分比。但是,当我在GetResponse
中呼叫FtpWebRequest
时,ContentLength
成员为-1。
好的 - 所以我使用WebRequestMethods.Ftp.GetFileSize
方法预先获得文件的大小。没问题。然后在获得大小后我下载文件。
这就是问题出现的地方......
获取大小后,我尝试重用FtpWebRequest
并将方法重置为WebRequestMethods.Ftp.DownloadFile
。这会导致System.InvalidOperationException
说“发送请求后无法执行此操作”。 (可能不是确切的表述 - 翻译自瑞典语)。
我在其他地方发现,只要我将KeepAlive
属性设置为true,就没关系,连接保持活动状态。这是我不明白的......我创建的唯一对象是我的FtpWebRequest
对象。如果我创建另一个,它怎么知道使用什么连接?什么凭据?
伪代码:
Create FtpWebRequest
Set Method property to GetFileSize
Set KeepAlive property to true
Set Credentials property to new NetworkCredential(...)
Get FtpWebResponse from the request
Read and store ContentLength
现在我得到了文件大小。所以是时候下载文件了。设置方法知道导致上述异常。那么我要创建一个新的FtpWebRequest
吗?或者无论如何重置请求重用? (结束回复没有任何区别。)
我不明白如何在不重新创建对象的情况下继续前进。我能做到这一点,但感觉不对劲。所以我在这里发帖,希望能找到正确的方法。
这是(非工作)代码(输入是sURI,sDiskName,sUser和sPwd。):
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(sURI);
request.Method = WebRequestMethods.Ftp.GetFileSize;
request.Credentials = new NetworkCredential(sUser, sPwd);
request.UseBinary = true;
request.UsePassive = true;
request.KeepAlive = true;
FtpWebResponse resp = (FtpWebResponse)request.GetResponse();
int contLen = (int)resp.ContentLength;
resp.Close();
request.Method = WebRequestMethods.Ftp.DownloadFile;
resp = (FtpWebResponse)request.GetResponse();
Stream inStr = resp.GetResponseStream();
byte[] buff = new byte[16384];
sDiskName = Environment.ExpandEnvironmentVariables(sDiskName);
FileStream file = File.Create(sDiskName);
int readBytesCount;
int readTotal=0;
while ((readBytesCount = inStr.Read(buff, 0, buff.Length)) > 0)
{
readTotal += readBytesCount;
toolStripProgressBar1.Value = 100*readTotal/contLen;
Application.DoEvents();
file.Write(buff, 0, readBytesCount);
}
file.Close();
我希望有人可以解释这是如何工作的。提前谢谢。
答案 0 :(得分:7)
我认为这不会得到回答所以我通过告诉你我是如何解决它而“关闭它”。
好吧,我没有真正解决它。然而,我确实通过重新创建FtpWebRequest
来测试下载,并注意到在FTP服务器上它的行为符合我的要求,即只有一个登录,然后顺序执行我的请求。
这是获取文件大小并开始下载的代码的结果:
// Start by fetching the file size
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(sURI);
request.Method = WebRequestMethods.Ftp.GetFileSize;
NetworkCredential nc = new NetworkCredential(sUser, sPwd);
request.Credentials = nc;
request.UseBinary = true;
request.UsePassive = true;
request.KeepAlive = true;
// Get the result (size)
FtpWebResponse resp = (FtpWebResponse)request.GetResponse();
Int64 contLen = resp.ContentLength;
// and now download the file
request = (FtpWebRequest)FtpWebRequest.Create(sURI);
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = nc;
request.UseBinary = true;
request.UsePassive = true;
request.KeepAlive = true;
resp = (FtpWebResponse)request.GetResponse();
如果可以重置FtpWebRequest
以便重复使用,那么就没有答案。但至少我知道没有多余的信息被转移。
感谢所有感兴趣并花时间思考答案的人。
答案 1 :(得分:3)
FtpWebRequest只能用于1个请求,例如获取文件大小或下载文件,但不能同时使用两者。您必须创建2个FtpWebRequests。在场景背后,FtpWebRequest注意到它是相同的URL和凭据,并且将重用相同的ftp连接而不关闭它,因为IsKeepAlieve为真,这是默认设置。
这是微软糟糕设计的一个悲惨例子。他们不是让我们明确地打开和关闭连接,而是希望自动为我们做这件事并让每个人感到困惑。
答案 2 :(得分:0)
您可能想要使用Async方法。这是MSDN文档的链接。
http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.aspx
GetResponseAsync()
这样可以防止您的应用程序锁定,因此您不必使用
Application.DoEvents();
您还可以查看可能使用替代ftp库。 imo the FtpWebRequest并不是最好的ftp类。快速搜索出现了这个库。 Ftp不像HTTP那样是无状态的。我更喜欢允许您创建客户端,打开连接以及保持连接活动的库。
http://sanity-free.org/dist/NullFX.Net-binary.zip
这是我找到的代码exacmple
FtpClient client =
new FtpClient(
new IPEndPoint( IPAddress.Loopback, 21 ),
new NetworkCredential( "test", "testing@localdomain" )
);
client.Connect();
client.Download("testfile.zip", @"C:\downloads\testfile.zip");
源也在那里,因此您可以将一些事件附加到读取过程以进行下载进度跟踪。