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
FtpWebRequestor any other built-in feature of .NET framework. TheFtpWebRequestunfortunately does not support theMLSDcommand, 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:
You use a long directory listing (
LISTcommand =ListDirectoryDetailsmethod) and try to parse a server-specific listing. Many FTP servers use *nix-style listing, where you identify a directory by thedat 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
MLSDcommand and/or parsing variousLISTlisting formats; and recursive downloads.For example with WinSCP .NET assembly you can use
Sesssion.ListDirectory:Internally, WinSCP uses the
MLSDcommand, if supported by the server. If not, it uses theLISTcommand and supports dozens of different listing formats.(I'm the author of WinSCP)