C # Uploading all files and subdirectories via FTP

general information
I'm still in the process of learning C #. To help myself, I am trying to create a program that automatically synchronizes all my local projects with a folder on my FTP server. It is so that, whether I am at school or at home, I always have the same projects that are available to me.

I know that there are programs like Dropbox that are already doing this for me, but I thought that creating something like this would teach me a lot along the way.

Problem
My first step toward my goal was to simply download all the files, subdirectories and subfiles from my FTP server. I was able to download all the files from the directory with the code below. However, my code lists the folder names and files in the main directory. Subfolders and subfiles never return and never load. In addition, the server returns a 550 error because I am trying to load folders as if they were files. I’ve been doing this for 4 hours, but I just can’t find anything about how to fix these problems and make them work. So I hope you guys help me :)

the 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"); } } 
+12
c # ftp ftpwebrequest
May 04 '16 at 9:50 pm
source share
1 answer

FtpWebRequest does not have explicit support for recursive file operations (including loading). You must implement recursion yourself:

  • Remote Directory List
  • Iterate over records, upload files and recurse to subdirectories (list them again, etc.).

The hard part is identifying files from subdirectories. There is no way to do this in a portable way using FtpWebRequest . Unfortunately, FtpWebRequest does not support the MLSD command, which is the only portable way to get a list of directories with file attributes in the FTP protocol. See Also Checking if an object on an FTP server is a file or directory .

Your options:

  • Perform an operation on the file name, which is likely to fail for the file and be successful for directories (or vice versa). That is, you can try to download the "name". If it succeeds, it is a file; if it fails, it is a directory.
  • You may be lucky, and in your particular case, you can specify a file from the directory by file name (i.e. all your files have an extension, but not in subdirectories).
  • You are using a long list of directories ( LIST command = ListDirectoryDetails method) and try to ListDirectoryDetails server related list. Many FTP servers use the * nix-style list, where you identify the directory with d at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the format is * nix)
 void DownloadFtpDirectory(string url, NetworkCredential credentials, string localPath) { FtpWebRequest listRequest = (FtpWebRequest)WebRequest.Create(url); listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails; listRequest.Credentials = credentials; List<string> lines = new List<string>(); using (FtpWebResponse listResponse = (FtpWebResponse)listRequest.GetResponse()) using (Stream listStream = listResponse.GetResponseStream()) using (StreamReader listReader = new StreamReader(listStream)) { while (!listReader.EndOfStream) { lines.Add(listReader.ReadLine()); } } foreach (string line in lines) { string[] tokens = line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries); string name = tokens[8]; string permissions = tokens[0]; string localFilePath = Path.Combine(localPath, name); string fileUrl = url + name; if (permissions[0] == 'd') { if (!Directory.Exists(localFilePath)) { Directory.CreateDirectory(localFilePath); } DownloadFtpDirectory(fileUrl + "/", credentials, localFilePath); } else { FtpWebRequest downloadRequest = (FtpWebRequest)WebRequest.Create(fileUrl); downloadRequest.Method = WebRequestMethods.Ftp.DownloadFile; downloadRequest.Credentials = credentials; using (FtpWebResponse downloadResponse = (FtpWebResponse)downloadRequest.GetResponse()) using (Stream sourceStream = downloadResponse.GetResponseStream()) using (Stream targetStream = File.Create(localFilePath)) { byte[] buffer = new byte[10240]; int read; while ((read = sourceStream.Read(buffer, 0, buffer.Length)) > 0) { targetStream.Write(buffer, 0, read); } } } } } 

Use the following function:

 NetworkCredential credentials = new NetworkCredential("user", "mypassword"); string url = "ftp://ftp.example.com/directory/to/download/"; DownloadFtpDirectory(url, credentials, @"C:\target\directory"); 



If you want to avoid problems with parsing directory list formats for a specific server, use a third-party library that supports the MLSD command and / or parsing various LIST list formats; and recursive downloads.

For example, building WinSCP.NET you can load an entire directory with a single call to Session.GetFiles :

 // Setup session options SessionOptions sessionOptions = new SessionOptions { Protocol = Protocol.Ftp, HostName = "ftp.example.com", UserName = "user", Password = "mypassword", }; using (Session session = new Session()) { // Connect session.Open(sessionOptions); // Download files session.GetFiles("/directory/to/download/*", @"C:\target\directory\*").Check(); } 

Internally, WinSCP uses the MLSD command, if supported by the server. If not, it uses the LIST command and supports dozens of different listing formats.

The Session.GetFiles method is recursive by default.

(I am the author of WinSCP)

+19
May 05 '16 at 6:18
source share



All Articles