List names of files in FTP directory and its subdirectories

12.1k views Asked by At

I have been searched in the net and I didn't found any result. Actually I want to get the name of all the files that I have in the root and Directory and Sub Directory. I tried the code as bellow but its give me only the files in the root of my FTP.

The folder that I have in the FTP is like as bellow:

/ds/product/Jan/
/ds/subproduct/Jan/
/ds/category/Jan/

The code that I tried:

FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create("ftp://" + FtpIP);
ftpRequest.Credentials = new NetworkCredential(FtpUser, FtpPass);
ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory;
FtpWebResponse response = (FtpWebResponse)ftpRequest.GetResponse();
StreamReader streamReader = new StreamReader(response.GetResponseStream());

List<string> directories = new List<string>();

string line = streamReader.ReadLine();
while (!string.IsNullOrEmpty(line))
{
   // directories.Add(line);
    line = streamReader.ReadLine().ToString();
    MessageBox.Show(line);
}

streamReader.Close();
1

There are 1 answers

1
Martin Prikryl On BEST ANSWER

It is s not easy to implement this without any external library. Unfortunately, neither the .NET Framework nor PowerShell have any explicit support for recursively listing files in an FTP directory.

You have to implement that yourself:

  • List the remote directory
  • Iterate the entries, recursing into subdirectories - listing them again, etc.

Tricky part is to identify files from subdirectories. There's no way to do that in a portable way with the .NET Framework (FtpWebRequest). The .NET Framework unfortunately does not support the MLSD 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:

  • Do an operation on a file name that is certain to fail for file and succeeds for directories (or vice versa). I.e. you can try to download the "name".
  • You may be lucky and in your specific case, you can tell a file from a directory by a file name (i.e. all your files have an extension, while subdirectories do not)
  • You use a long directory listing (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 the d at the very beginning of the entry. But many servers use a different format. The following example uses this approach (assuming the *nix format)
static void ListFtpDirectory(string url, NetworkCredential credentials)
{
    WebRequest listRequest = WebRequest.Create(url);
    listRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
    listRequest.Credentials = credentials;

    List<string> lines = new List<string>();

    using (WebResponse listResponse = listRequest.GetResponse())
    using (Stream listStream = listResponse.GetResponseStream())
    using (StreamReader listReader = new StreamReader(listStream))
    {
        while (!listReader.EndOfStream)
        {
            string line = listReader.ReadLine();
            lines.Add(line);
        }
    }

    foreach (string line in lines)
    {
        string[] tokens =
            line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
        string name = tokens[8];
        string permissions = tokens[0];

        if (permissions[0] == 'd')
        {
            Console.WriteLine($"Directory {name}");

            string fileUrl = url + name;
            ListFtpDirectory(fileUrl + "/", credentials);
        }
        else
        {
            Console.WriteLine($"File {name}");
        }
    }
}

Use the function like:

NetworkCredential credentials = new NetworkCredential("user", "mypassword");
string url = "ftp://ftp.example.com/directory/to/list/";
ListFtpDirectory(url, credentials);

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 various LIST listing formats.

For example with WinSCP .NET assembly you can list whole directory recursively with a single call to Session.EnumerateRemoteFiles:

// Setup session options
var sessionOptions = new SessionOptions
{
    Protocol = Protocol.Ftp,
    HostName = "ftp.example.com",
    UserName = "user",
    Password = "mypassword",
};

using (var session = new Session())
{
    // Connect
    session.Open(sessionOptions);

    // Enumerate files
    var options =
        EnumerationOptions.EnumerateDirectories |
        EnumerationOptions.AllDirectories;
    IEnumerable<RemoteFileInfo> fileInfos =
        session.EnumerateRemoteFiles("/directory/to/list", null, options);
    foreach (var fileInfo in fileInfos)
    {
        Console.WriteLine(fileInfo.FullName);
    }
}

Not only the code is simpler, more robust and platform-independent. It also makes all other file attributes (size, modification time, permissions, ownership) readily available via the RemoteFileInfo class.

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.

(I'm the author of WinSCP)