Reusing FtpWebRequest

I am trying to make an easy way to upload a file from FTP using FtpWebRequest using the WebRequestMethods.Ftp.DownloadFile method. The problem is that I will not show the progress of the download and therefore must know the file size ahead in order to be able to calculate the percentage percentage. But when I call GetResponse in FtpWebRequest , the ContentLength member is -1.

OK - that's why I pick up the file size in advance using the WebRequestMethods.Ftp.GetFileSize method. No problems. Then after getting the size, I upload the file.

This is where the problem arises ...

After getting the size, I try to reuse FtpWebRequest and resets the method to WebRequestMethods.Ftp.DownloadFile . This causes the System.InvalidOperationException say something like "This action cannot be completed after sending the request." (may not be the exact wording translated from the one I receive in Swedish).

I found elsewhere that while I set the KeepAlive property to true, it does not matter, the connection remains active. This is what I do not understand ... The only object I created is my FtpWebRequest object. And if I create another one, how can he know which connection to use? And what are the credentials?

Pseudocode:

 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 

Now I got the file size. So it's time to upload the file. Knowing the configuration method raises the above exception. So am I creating a new FtpWebRequest ? Or is it anyway that the reset request is reused? (Closing the answer didn't make any difference.)

I do not understand how to move forward without re-creating the object. I could do it, but it's just not true. Therefore, I am posting here to find the right way to do this.

Here is the (non-working) code (inputs are sURI, sDiskName, sUser and 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(); 

Hope someone can explain how this should work. Thanks in advance.

+8
c # ftp ftpwebrequest
source share
3 answers

I do not think that this will be answered, so I "close it" by telling you how I decided it.

Well, I really didn't solve it. However, I tested the download by recreating FtpWebRequest and noticed that on the FTP server it behaved as I wanted, that is, there was only one login and the subsequent execution of my requests.

Thus, the code that received the file size and the start of the download turned out to be:

 // 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(); 

Therefore, there is no answer to the question of whether it is possible to reset FtpWebRequest for reuse. But at least I know that redundant information is not transmitted.

Thanks to everyone who showed interest and took the time to think about the answer.

+6
source share

FtpWebRequest can be used for only one request, for example, to get the file size or to download a file, but not for both. You need to create 2 FtpWebRequests. Behind the scene, FtpWebRequest notes that this is the same URL and credentials, and will reuse the same ftp connection without closing it, since IsKeepAlieve is true, which is the default setting.

This is a sad example of poor design from Microsoft. Instead of letting us open and close the connection openly, they want to do it automatically for us and confuse everyone.

+3
source share

You probably want to use the Async method. Here is a link to the MSDN document.

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

 GetResponseAsync() 

It will also make your application block, so you don’t have to use

 Application.DoEvents(); 

You can also look, possibly using an alternative ftp library. imo FtpWebRequest is not the best ftp class. A quick search revealed this library. Ftp is not stateless like HTTP. I prefer libraries that allow you to create a client, open a connection, and maintain a network connection.

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

Here's the exacmple code I found

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

There is a source too, so you could attach some events to the reading process to track the download progress.

0
source share

All Articles