1

I am trying to download a file via FTP, using the WebClient class. The problem is, one of the directory comprising the file path on the FTP server contains whitespaces.

And, even though my URI string has whitespaces, they are all converted to "%20" automatically when using the DownloadData method, therefore the directory isn't found and the download fails.

My question is: Is there a way to force the WebClient not to convert my whitespaces into %20 ?

Here is my code:

using (WebClient request = new WebClient())
{
    request.Credentials = new NetworkCredential("login", "password");
    string url = ("ftp://XXX.XXX.XX.XXX:21/root/folder1/folder with spaces/pic1.jpg");
    byte[] fileData = request.DownloadData(url);
    using (FileStream file = File.Create(localpath))
    {
        file.Write(fileData, 0, fileData.Length);
        file.Close();
    }
}

Using Wireshark, this is my network log:

Request: USER login
Response: 331 Please specify the password
Request: PASS password
Response: 230 Login successful
Request: OPTS utf8 on
Response: 200 Always in UTF8 mode.
Request: PWD
Response: 257 "/home/login"
Request: CWD home/login/root/folder1/folder%20with%20spaces/
Response: 550 Failed to change directory 

Log for trying to download folder1 (note that CWD did not fail, I just got an error 'cause it expected a file and not a folder):

Response: 230 Login successful
Request: OPTS utf8 on
Response: 200 Always in UTF8 mode.
Request: PWD
Response: 257 "/home/login"
Request: CWD home/login/root/
Response: 250 Directory successfully changed
Request: RETR folder1
Response: 550 Failed to open file.

Log for downloading pic1.jpg with FileZilla (note that the difference is that there are no %20 in the file path):

Response: 230 Login successful
Request: OPTS UTF8 on
Response: 200 Always in UTF8 mode.
Request: CWD home/login/root/folder1/folder with spaces/
Response: 250 Directory successfully changed
Request: RETR pic1.jpg
Response: 150 Opening BINARY mode data connection for pic1.jpg
Response: 226 Transfer complete.
Ljotur
  • 19
  • 4
  • You can just URL Decode the value before using it as a file path. – Crowcoder Jan 02 '18 at 18:11
  • Show us [.NET network log file](https://stackoverflow.com/q/9664650/850848) – Martin Prikryl Jan 02 '18 at 18:18
  • 3
    What is `filePath` value? + What exception are you getting? – Martin Prikryl Jan 02 '18 at 18:19
  • filePath is "/folder1/folder with spaces/picture.jpg" The exception is "WebException: Server returned an error: 550 Failed to change directory" – Ljotur Jan 02 '18 at 18:52
  • It would be more obvious if you just replace `filePath` in your sample code with an actual path containing whitespace. – Rufus L Jan 02 '18 at 19:03
  • Using Wireshark, this is my network log: Request: USER login Response: 331 Please specify the password Request: PASS password Response: 230 Login successful Request: PWD Response: 257 "/home/login" Request: CWD home/login/root/folder1/folder with spaces/ Response: 550 Failed to change directory – Ljotur Jan 02 '18 at 19:04
  • Are you using .Net 4.5? The Uri handling changed... – NetMage Jan 02 '18 at 22:00
  • I'm using .NET 4.6 – Ljotur Jan 03 '18 at 17:07

3 Answers3

0

If you need to access a path that is not relative to the home folder, you need to use two slashes:

string url = "ftp://XXX.XXX.XX.XXX:21//root/folder1/folder with spaces/pic1.jpg";

or a relative path like:

string url = "ftp://XXX.XXX.XX.XXX:21/../../root/folder1/folder with spaces/pic1.jpg";

(the problem has nothing to do with the spaces)

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
  • I thought about this too, but I can access root and folder1 just fine, and I tried to access another picture with a similar path (but without any whitespaces) and it's working like a charm... – Ljotur Jan 02 '18 at 19:54
  • Post a log file accessing `folder1`. + Can you access `folder with spaces` using any (even GUI) FTP client? – Martin Prikryl Jan 02 '18 at 20:01
  • Yeah I can access the folder just fine with FileZilla or my browser. Here is the log : ------- Request: PWD Response: 257 "/home/login" Request: CWD home/login/root/ Response: 250 Directory successfully changed Request: RETR folder1 Response: 550 Failed to open file. -------- I still get an error 'cause it's a folder and not a file, but the important part is that the CWD didn't fail. – Ljotur Jan 02 '18 at 20:11
  • Edit the log into your question + Show us FileZilla log file for both folders. – Martin Prikryl Jan 02 '18 at 20:14
  • Done. I didn't put the log for trying to download folder1 with FileZilla 'cause it automatically downloads all the files recursively, so no comparison can be made – Ljotur Jan 02 '18 at 20:24
  • Your log file do not look anything like mine. For me, your code does not use `CWD`. On the contrary your log misses `OPTS utf8 on` command. What version on .NET are you using? Post .NET network log file (as I've requested in a comment to your question). – Martin Prikryl Jan 02 '18 at 20:41
  • I'm using .NET 4.6, and I'm programming in Unity (kinda noob), so I don't really know how to configure an App.config file in it, sorry... – Ljotur Jan 02 '18 at 20:46
  • .NET 4.6 sends `OPTS utf8 on` command on login unconditionally, and does not send `CWD` before `RETR`, so somewhat doubt that you are showing us a correct log file. + So start with a simple .NET console application - We need [mcve]. – Martin Prikryl Jan 02 '18 at 20:51
  • Yeah I ommited the utf8 part 'cause I thought it was irrelevant, sorry I added it now. But it definitely does send a CWD before RETR, at least on my computer – Ljotur Jan 02 '18 at 20:56
  • Again, please use a simple .NET console application for the test. The network log may contain some useful information (in addition to actual FTP log). – Martin Prikryl Jan 02 '18 at 20:57
0

Can you try creating your Uri manually with Uri(string, bool)?

using (WebClient request = new WebClient())
{
    request.Credentials = new NetworkCredential("login", "password");
    string url = "ftp://XXX.XXX.XX.XXX:21/root/folder1/folder with spaces/pic1.jpg";
    var uri = new Uri(url, true);
    byte[] fileData = request.DownloadData(uri);
    using (FileStream file = File.Create(localpath))
    {
        file.Write(fileData, 0, fileData.Length);
        file.Close();
    }
}

Note that Uri(string, bool) is declared obsolete, so presumably it will be removed from the framework at some point.

NetMage
  • 26,163
  • 3
  • 34
  • 55
-3

If there a reason you aren't using WebClient.DownloadFile?

using (WebClient request = new WebClient())
{
   request.Credentials = new NetworkCredential("login", "password");
   string url = ("ftp://XXX.XXX.XX.XXX:21/root" + filePath);
   request.DownloadFile(url, localpath);
 }
R.Laney
  • 174
  • 8
  • 2
    How does this answer the question? OP does not have problems with writing to a local file, but with downloading the file. – Martin Prikryl Jan 02 '18 at 18:19