简体   繁体   中英

Check if is file or folder on FTP

I read list of files/folders from FTP.

Problem is that I don't know if is file or folder. Currently I am checking if string have extension. If yes than it is file else it is folder. But this is not good enough, it can exist file without extension and folder with extension (eg. folder name could be FolderName.TXT)

This is code I use to list content of folder:

public async Task<CollectionResult<string>> ListFolder(string path)
{
    try
    {
        FtpWebRequest ftpRequest = null;
        var fileNames = new List<string>();
        var res = new CollectionResult<string>();
        ftpRequest = ftpBuilder.Create(path, WebRequestMethods.Ftp.ListDirectory);
        using (var ftpResponse = (FtpWebResponse)await ftpRequest.GetResponseAsync())
        using (var ftpStream = ftpResponse.GetResponseStream())
        using (var streamReader = new StreamReader(ftpStream, Encoding.UTF8))
        {
            string fileName = streamReader.ReadLine();
            while (!string.IsNullOrEmpty(fileName))
            {
                fileNames.Add(Path.Combine(path, fileName.Substring(fileName.IndexOf('/') + 1, fileName.Length - fileName.IndexOf('/') - 1)));
                fileName = streamReader.ReadLine();
            }
        }
        ftpRequest = null;
        res.ListResult = fileNames;
        return res;
    }
    catch (Exception e)
    {
        e.AddExceptionParameter(this, nameof(path), path);
        throw;
    }
}

It would be best if I could detect if is file or folder inside while loop, but it is not possible to do this just from string.

Thank you for your help.


EDIT

I found similar question. C# FTP, how to check if a Path is a File or a Directory? But question is very old and there is no nice solution.


Edit: Solution

 public async Task<CollectionResult<Tuple<string, bool>>> ListFolder(string path)
        {
            try
            {
                FtpWebRequest ftpRequest = null;
                var fileNames = new CollectionResult<Tuple<string, bool>>();
                fileNames.ListResult = new List<Tuple<string, bool>>();
                if (!(IsFtpDirectoryExist(path)))
                {
                    throw new RemoteManagerWarningException(ErrorKey.LIST_DIRECTORY_ERROR, fileNames.ErrorMessage = $"path folder {path} not exists");
                }
                ftpRequest = ftpBuilder.Create(path, WebRequestMethods.Ftp.ListDirectoryDetails);
                using (var ftpResponse = (FtpWebResponse)await ftpRequest.GetResponseAsync())
                using (var ftpStream = ftpResponse.GetResponseStream())
                using (var streamReader = new StreamReader(ftpStream, Encoding.UTF8))
                {
                    while (!streamReader.EndOfStream)
                    {
                        string line = streamReader.ReadLine();
                        string[] tokens = line.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
                        // is number:
                        Regex rgx = new Regex(@"^[\d\.]+$");
                        var isExternalFtpOrUnixDirectoryStyle = !(rgx.IsMatch(line[0].ToString()));
                        string name = string.Empty;
                        bool isFolder = false;

                        if (isExternalFtpOrUnixDirectoryStyle)
                        {
                            name = tokens[8];
                            var permissions = tokens[0];
                            isFolder = permissions[0] == 'd';
                        }
                        else
                        {
                            tokens = line.Split(new[] { ' ' }, 4, StringSplitOptions.RemoveEmptyEntries);
                            name = tokens[3];
                            isFolder = tokens[2] == "<DIR>";
                        }
                        name = Path.Combine(path, name);
                        Tuple<string, bool> tuple = new Tuple<string, bool>(name, isFolder);
                        fileNames.ListResult.Add(tuple);                           
                    }
                }
                ftpRequest = null;
                return fileNames;
            }
            catch (Exception e)
            {
                e.AddExceptionParameter(this, nameof(path), path);
                throw;
            }
        }

There's no way to identify if a directory entry is a sub-directory of file in a portable way with the FtpWebRequest or any other built-in feature of .NET framework. The FtpWebRequest 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 a file and succeeds for a directory (or vice versa). Ie you can try to download the "name". If that succeeds, it's a file, if that fails, it's a directory.
  • You may be lucky and in your specific case, you can tell a file from a directory by a file name (ie 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.

    For some examples of implementing the parsing, see:
    *nix format: Parsing FtpWebRequest ListDirectoryDetails line
    DOS/Windows format: C# class to parse WebRequestMethods.Ftp.ListDirectoryDetails FTP response


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; and recursive downloads.

For example with WinSCP .NET assembly you can use Sesssion.ListDirectory :

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

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

    RemoteDirectoryInfo directory = session.ListDirectory("/home/martin/public_html");

    foreach (RemoteFileInfo fileInfo in directory.Files)
    {
        if (fileInfo.IsDirectory)
        {
            // directory
        }
        else
        {
            // file
        }
    }
}

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)

使用FTP'list'命令并解析权限和目录指示符。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM