10

我正在尝试制作一个简单的方法来使用FtpWebRequest该方法从 FTP 下载文件WebRequestMethods.Ftp.DownloadFile。问题是我不想显示下载进度,因此需要提前知道文件大小才能计算传输的百分比。但是当我打电话GetResponse给成员时FtpWebRequestContentLength-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();

我希望有人可以解释这应该如何工作。提前致谢。

4

3 回答 3

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供重复使用没有任何答案。但至少我知道没有多余的信息被传输。

感谢所有感兴趣并花时间思考答案的人。

于 2013-07-10T13:33:25.647 回答
5

FtpWebRequest 只能用于 1 个请求,例如获取文件大小或下载文件,但不能同时用于两者。您必须创建 2 个 FtpWebRequest。在幕后,FtpWebRequest 注意到它是相同的 URL 和凭据,并将重用相同的 ftp 连接而不关闭它,只要 IsKeepAlieve 为 true,这是默认设置。

这是微软糟糕设计的一个可悲例子。他们不想让我们明确地打开和关闭连接,而是想自动为我们做这件事,让每个人都感到困惑。

于 2013-08-07T02:47:19.310 回答
0

您可能想要使用 Async 方法。这是 MSDN 文档的链接。

http://msdn.microsoft.com/en-us/library/system.net.ftpwebrequest.aspx

GetResponseAsync()

这将使您的应用程序也不会锁定,因此您不必使用

Application.DoEvents();

您还可以考虑使用替代的 ftp 库。imo FtpWebRequest 并不是最好的 ftp 类。快速搜索出现了这个库。Ftp 不像 HTTP 那样是无状态的。我更喜欢允许您创建客户端、打开连接并保持连接活动的库。

http://sanity-free.org/dist/NullFX.Net-binary.zip

这是我找到的代码示例

FtpClient client = 
    new FtpClient( 
        new IPEndPoint( IPAddress.Loopback, 21 ), 
        new NetworkCredential( "test", "testing@localdomain" ) 
    );
client.Connect();
client.Download("testfile.zip", @"C:\downloads\testfile.zip");

源也在那里,因此您可以将一些事件附加到读取过程中以进行下载进度跟踪。

于 2013-07-03T15:33:27.807 回答