General Info
I'm still in the process of learning C#. To help myself out, I'm trying to create a program that will automatically synchronise all of my local projects with a folder on my FTP server. This so that whether I'm at school or at home, I always have the same projects available to me.
I know there are programs like Dropbox that already do this for me, but I figured creating something like that myself will teach me a lot along the way.
The problem
My first step towards my goal was to just download all files, subdirectories and subfiles from my FTP server. I've managed to download all files from a directory with the code below. However, my code only lists the folder names and the files in the main directory. Subfolders and subfiles are never returned and never downloaded. Aside from that, the server returns a 550 error because I'm trying to download the folders as if they are files. I've been on this for 4+ hours now, but I just can't find anything on how to fix these problems and make it work. Therefor I'm hoping you guys will help me out :)
Code
public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
WebResponse response = null;
StreamReader reader = null;
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.ListDirectory;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
response = request.GetResponse();
reader = new StreamReader(response.GetResponseStream());
string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
result.Remove(result.ToString().LastIndexOf('\n'), 1);
return result.ToString().Split('\n');
}
catch (Exception ex)
{
if (reader != null)
{
reader.Close();
}
if (response != null)
{
response.Close();
}
downloadFiles = null;
return downloadFiles;
}
}
private void Download(string file)
{
try
{
string uri = url + "/" + file;
Uri serverUri = new Uri(uri);
if (serverUri.Scheme != Uri.UriSchemeFtp)
{
return;
}
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url + "/" + file);
request.UseBinary = true;
request.Method = WebRequestMethods.Ftp.DownloadFile;
request.Credentials = new NetworkCredential(ftpUserName, ftpPassWord);
request.KeepAlive = false;
request.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
FileStream writeStream = new FileStream(localDestnDir + "\\" + file, FileMode.Create);
int Length = 2048;
Byte[] buffer = new Byte[Length];
int bytesRead = responseStream.Read(buffer, 0, Length);
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = responseStream.Read(buffer, 0, Length);
}
writeStream.Close();
response.Close();
}
catch (WebException wEx)
{
MessageBox.Show(wEx.Message, "Download Error");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Download Error");
}
}
The
FtpWebRequest
does not have any explicit support for recursive file operations (including downloads). You have to implement the recursion yourself:Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the
FtpWebRequest
. TheFtpWebRequest
unfortunately does not support theMLSD
command, which is the only portable way to retrieve directory listing with file attributes in FTP protocol. See also Checking if object on FTP server is file or directory.Your options are:
LIST
command =ListDirectoryDetails
method) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by thed
at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format)Use the function like:
If you want to avoid troubles with parsing the server-specific directory listing formats, use a 3rd party library that supports the
MLSD
command and/or parsing variousLIST
listing formats; and recursive downloads.For example with WinSCP .NET assembly you can download whole directory with a single call to the
Session.GetFiles
:Internally, WinSCP uses the
MLSD
command, if supported by the server. If not, it uses theLIST
command and supports dozens of different listing formats.The
Session.GetFiles
method is recursive by default.(I'm the author of WinSCP)