0 votes
by (240 points)
edited

How can we use regular expressions in GetFiles and PutFiles methods? Currently wildcards are only supported.

Applies to: Rebex SFTP, Rebex FTP/SSL
by (240 points)
edited

Please help me to solve this issue. I have one very urgent release and this appears as a show stopper for me.

2 Answers

+2 votes
by (62.7k points)
edited
 
Best answer

In the Release 2012 R1 the Rebex.IO.FileSet class was introduced. It can be used in all multi-file methods which are: Upload, Download, Delete and GetItems.

The Rebex.IO.FileSet class uses wildcards, but you can implement your own descendants to support regular expressions. It can be done as follows:

/// <summary>
/// FileSet matches files under BasePath (and all subdirectories) specified by a Regex.
/// </summary>
public class SimpleFileSet_DeepFileRegexMatch : FileSet
{
    string _includePattern;

    public SimpleFileSet_DeepFileRegexMatch(string basePath, string includePattern)
        : base(basePath)
    {
        _includePattern = includePattern;
    }

    public override bool IsMatch(string relativePath, FileSetMatchMode mode)
    {
        string filename = Path.GetFileName(relativePath);
        switch (mode)
        {
            case FileSetMatchMode.MatchFile:
                RegexOptions options = IsCaseSensitive ? 0 : RegexOptions.IgnoreCase;
                return Regex.IsMatch(filename, _includePattern, options);
            case FileSetMatchMode.MatchDirectory:
                return false;
            case FileSetMatchMode.TraverseDirectory:
                return true;
            default:
                throw new ArgumentOutOfRangeException("mode");
        }
    }
}

It can be used as follows:

// create set matching all .txt files under "C:/test1" directory
SimpleFileSet_DeepFileRegexMatch set = 
    new SimpleFileSet_DeepFileRegexMatch("C:/test1", "[.]txt$");

// print what the set matches
foreach (FileSystemItem item in set.GetLocalItems())
{
    Console.WriteLine(item.Path);
}

// prepare client for upload
Sftp client = new Sftp();
client.Connect(...);
client.Login(...);

// upload files according the set
client.Upload(set, "upload");

// set BasePath to the remote directory
set.BasePath = "upload";

// print what the set matches on the remote host
foreach (FileSystemItem item in client.GetItems(set))
{
    Console.WriteLine(item.Path);
}

// download matching files to local directory
client.Download(set, "C:/test2");
+1 vote
by (62.7k points)
edited

Regular expressions are not supported in GetFiles/PutFiles methods, but it is a feature we are currently working on. It will be added in new release.

In the mean time you can use following simple implementation of GetFiles method which uses a regular expression to filter files/directories to be transferred.

Please leave a comment if you need a VB.NET code or if you want also the PutFiles method.

static void Main(string[] args)
{
    Ftp client = new Ftp();
    client.Connect(...);
    client.Login(...);

    GetFiles(client, "/data", @"c:\temp", new Regex("file[0-2].txt"), new Regex("dir(0|1)"));

    client.Disconnect();
}

private static void GetFiles(Ftp client, string remotePath, string localPath, Regex fileNameFilter, Regex directoryNameFilter)
{
    List<string> list = GetListRecursive(client, remotePath, fileNameFilter, directoryNameFilter);

    foreach (string remote in list)
    {
        string local = Path.Combine(localPath, GetRelativePath(remote, remotePath));
        string localDirectory = Path.GetDirectoryName(local);

        if (!Directory.Exists(localDirectory))
            Directory.CreateDirectory(localDirectory);

        client.GetFile(remote, local);
    }
}

private static List<string> GetListRecursive(Ftp client, string remotePath, Regex fileNameFilter, Regex directoryNameFilter)
{
    List<string> paths = new List<string>();

    client.ChangeDirectory(remotePath);

    FtpList list = client.GetList();
    foreach (FtpItem item in list)
    {
        string name = item.Name;
        string path = FtpPathCombine(remotePath, name);

        switch (item.Type)
        {
            case FtpItemType.Directory:
                if (directoryNameFilter.IsMatch(name))
                    paths.AddRange(GetListRecursive(client, path, fileNameFilter, directoryNameFilter));
                break;
            case FtpItemType.File:
                if (fileNameFilter.IsMatch(name))
                    paths.Add(path);
                break;
        }
    }

    return paths;
}

private static string FtpPathCombine(string path1, string path2)
{
    return string.Format("{0}/{1}", path1.TrimEnd('/'), path2.TrimStart('/'));
}

private static string GetRelativePath(string path, string basePath)
{
    return path.Substring(basePath.Length).TrimStart('/', '\\');
}
...