Reputation: 4009
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;
}
}
Upvotes: 1
Views: 3811
Reputation: 202721
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:
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)
Upvotes: 2
Reputation: 4348
Use FTP 'list' command and parse the permission and directory indicator.
Upvotes: 1