Reputation: 67345
I wrote the following code to detect if a directory exists. The DirectoryExists
method accepts either a fully qualified or relative path.
public bool DirectoryExists(string directory)
{
try
{
FtpWebRequest request = GetRequest(directory);
request.Method = WebRequestMethods.Ftp.ListDirectory;
using (FtpWebResponse response = request.GetResponse() as FtpWebResponse)
using (StreamReader sr = new StreamReader(response.GetResponseStream(), System.Text.Encoding.ASCII))
{
sr.ReadToEnd();
}
return true;
}
catch { }
return false;
}
protected FtpWebRequest GetRequest(string filename = "")
{
FtpWebRequest request = WebRequest.Create(_host.GetUrl(filename)) as FtpWebRequest;
request.Credentials = new NetworkCredential(Username, Password);
request.Proxy = null;
request.KeepAlive = false;
return request;
}
Note: _host.GetUrl(filename)
returns the fully qualified path of the specified directory or filename. In my case, this is ftp://www.mydomain.com/Articles/controls/precisely-defining-kilobytes-megabytes-and-gigabytes
.
This code has worked for many months. But all of a sudden it stopped working. Now, there is no exception raised in DirectoryExists
when the directory does not exist. sr.ReadToEnd()
simply returns an empty string.
I posted a similar question and it was suggested that I should always append /
to the end of my path. I tried that, and thought I got an exception once, but it's not raising an exception now.
I don't know if the behavior changed on the FTP server I'm communicating with or what. Again, this worked fine when I wrote it and now it doesn't.
How can I determine whether or not an FTP directory exists?
EDIT:
Inspecting the response after calling ReadToEnd()
, I see:
BannerMessage="220 Microsoft FTP Service\r\n"
ContentLength=-1
ExitMessage="221 Goodbye.\r\n"
StatusCode=ClosingData
StatusDescription="226 Transfer complete.\r\n"
WelcomeMessage="230 User logged in.\r\n"
UPDATE:
Ultimately, I see that most people have been recommending variations of what I was doing originally. This has lent weight to Hans Passant's suggestion that the issue lies with the server. I am attempting to get them to look at this but they seem a little baffled by the entire discussion. I know they are using a Microsoft server, and I'm skeptical I will be able to get a resolution from them.
If all else fails, I think the answer is to do a listing of the parent directory, which does require some extra work to handle cases such as when the directory in question is the root, and also cases when the parent doesn't exist.
Upvotes: 5
Views: 7346
Reputation: 15193
One way is to list the directory content as present in many solutions, but in my case i also have to read data content so the server checks the directory. If the directory is not present, an webException is throw that should be interpreted to filter from unexpected errors.
Here is my solution:
bool directoryExists(string path)
{
bool? exists = null;
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(path);
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential("username", "*****");
FtpWebResponse response = null;
try
{
response = (FtpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
string str = sr.ReadLine(); //Just needs to read one line to fire check on server
sr.Close();
return true;
}
}
catch (WebException ex)
{
var r = (FtpWebResponse)ex.Response;
if (r.StatusCode ==
FtpStatusCode.ActionNotTakenFileUnavailable)
{
//Does not exist
return false;
}
throw; //if error is not related to directory existence then the exception needs to be treated above.
}
finally
{
if (response != null)
response.Close();
}
return false;
}
The FTP server i am targeting i know to be a windows server but i don't have access to the version information.
Upvotes: 0
Reputation: 29
I use the below to check if a ftp connection credentials are valid. You may use the same to check if directory exists. Returns true if url, username, and password are correct.
URL: you specify your directory path in here. user: ftp username password: ftp password
public static bool isValidConnection(string url, string user, string password, ref string errorMessage = "")
{
try {
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(user, password);
request.KeepAlive = false;
request.UsePassive = true;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
response.Close();
} catch (WebException ex) {
errorMessage = ex.Message;
return false;
}
return true;
}
Upvotes: 0
Reputation:
FTP servers reply with a 550 message when you ask for a non existing directory/file. This 550 is translated to an exception in .NET.
Regarding the code you presented I don't see the reason to use a StreamReader in production. A simple modification is the following:
public bool DirectoryExists(string directory)
{
try
{
FtpWebRequest request = GetRequest(directory);
request.Method = WebRequestMethods.Ftp.ListDirectory;
return request.GetResponse() != null;
}
catch
{
return false;
}
}
I kept the exception handling as is (missing) but I suggest you to work on it as there are many and different cases to catch that are not related to ftp responses.
request.GetResponse() will generate an exception if the directory does not exist.
I tried it with the following paths for true returns:
The last one is an existing but empty directory
ftp://ftp.mozilla.org/pub/data/bloat-reports2/ returns false
There is a big but regarding trailing / in paths.
mozilla.org is a zero byte length file under pub directory
ftp://ftp.mozilla.org/pub/mozilla.org returns true
ftp://ftp.mozilla.org/pub/mozilla.org/ returned false and then true ?????
This behavior looks like with what you described. You can easily reproduce it on a browser. I suggest you to do the same with your own paths and check the contents of the directories. In case you do have empty files, remove them or replace their contents with a space, and finally check your code to ensure that you will not create new files or recreate them with zero byte length.
I hope that this helps.
UPDATE
No news, bad news! I assume that nothing of the above helped you. Maybe by going one level up in your path will solve the problem. The idea is to get a list of the parent directory and check it for the child name. If the parent path is valid it should always return non empty string. The code is the following:
static bool DirectoryExists(string directory)
{
try
{
directory = GetRequest(directory);
string
parent = directory.Substring(0, directory.TrimEnd('/').LastIndexOf('/') + 1),
child = directory.Substring(directory.TrimEnd('/').LastIndexOf('/') + 1).TrimEnd('/');
FtpWebRequest request = GetRequest(parent);
request.Method = WebRequestMethods.Ftp.ListDirectory;
using (FtpWebResponse response = request.GetResponse() as FtpWebResponse)
{
if (response == null)
return false;
string data = new StreamReader(response.GetResponseStream(), true).ReadToEnd();
return data.IndexOf(child, StringComparison.InvariantCultureIgnoreCase) >= 0;
}
}
catch
{
return false;
}
}
As you can see there is no need to worry about trailing slashes.
Warning: The code will not cover the case of a subdirectory and a filename with same names under the same path.
Upvotes: 9
Reputation: 2882
I had similar code that would deploy items via FTP to an IIS 6/Windows 2003 Server. This worked fine until I pointed it at an IIS7/Windows 2008 server. I started seeing the exact same behavior you did. I debugged it and ultimately changed my code to the following. This is a direct snippet from my code. I can include the source from the methods it calls if you like, but I think you understand those operations.
try
{
//Get a recursive FTP Listing
items = GetFtpListing(target, true);
}
catch(WebException e)
{
if (string.Equals(e.Message, "The remote server returned an error: (550) File unavailable (e.g., file not found, no access)."))
{
CreateRemoteDirectory(target);
items = GetFtpListing(target, true);
}
else
{
throw e;
}
}
relevant section of GetFtpListing
List<string> ftpRawList = new List<string>();
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(path);
request.Timeout = 60000;
request.ReadWriteTimeout = 60000;
request.Credentials = this.credentials;
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
Stream data = response.GetResponseStream();
using (StreamReader reader = new StreamReader(data))
{
string responseString = reader.ReadToEnd();
ftpRawList.AddRange(responseString.Split(new char[2] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries));
}
Upvotes: 0
Reputation: 634
This might happen because FTP server software installed on your server.
Otherwise, you may want to take a look at response.StatusCode to see if there is any hints.
http://msdn.microsoft.com/en-us/library/system.net.ftpstatuscode(v=vs.110).aspx
Finally, if all of that doesn't work, try to write a dumb text file on that un-existed directory.
Upvotes: 0