Search code examples
c#asp.netwinscpwinscp-net

WinSCP.Net, folder is not copied from remote to local


I have been using WinSCP to download files, periodically, from a Unix server to Windows server and it has been working with no issues. I also check if remote file is older or already exists (don't copy).

Now, I have to do the same but this time I have to download files and folders. Files are copied fine but folders aren't. When playing with the settings, I got it to copy the contents of the folder but they get copied to my root local folder; I thought WinSCP would copy everything.

In below code, LocalFolder is Z:\My_Data and LogRootFolder is /xyz/gtc/a00/

Folder structure on remote is /xyz/gtc/a00/ABCD/outcomes/ with subfolder "backup" that has many subfolders named as dates (e.g. /xyz/gtc/a00/ABCD/outcomes/backup/2021-06-23/)

Either none of "backup/2021-xx-xx/" files and folder are copied, or they are all copied to Z:\My_Data\ABCD

After setting up the session, called SFTP_Session:

string sRemotePath = LogRootFolder + "ABCD/outcomes/";
string sLocalFolder = Path.Combine(LocalFolder, @"ABCD\");

if (SFTP_Session.Opened)
{
    using (SFTP_Session)
    {
        SFTP_Session.QueryReceived += (sender, e) =>
        {
            ...
            e.Continue();
        };                                                
        //var opts = EnumerationOptions.EnumerateDirectories | EnumerationOptions.AllDirectories;
        //IEnumerable<RemoteFileInfo> fileInfos = SFTP_Session.EnumerateRemoteFiles(sRemotePath, "*.dat", opts);  <-- This copies files in folder(s) to m local root folder
        Regex mask = new Regex(@"\.(dat|err)$", RegexOptions.IgnoreCase);
        IEnumerable<RemoteFileInfo> fileInfos =
            SFTP_Session.EnumerateRemoteFiles(sRemotePath, null, EnumerationOptions.AllDirectories)
            .Where(fileInfo => mask.Match(fileInfo.Name).Success)
            .ToList();

    foreach (RemoteFileInfo fileInfo in fileInfos)
    {
        string localFilePath = Path.Combine(sLocalFolder, fileInfo.Name);

        if (fileInfo.IsDirectory)
        {
            // Create local subdirectory, if it does not exist yet
            if (!Directory.Exists(localFilePath))
            {
                Directory.CreateDirectory(localFilePath);
            }
        }
        else
        {
            string remoteFilePath = RemotePath.EscapeFileMask(fileInfo.FullName);

            // If file does not exist in local folder, download
            if (!File.Exists(localFilePath))
            {
                bDownload = true;
            }
            else    // If file exists in local folder but is older, download; else skip
            {
                DateTime remoteWriteTime = SFTP_Session.GetFileInfo(remoteFilePath).LastWriteTime;
                DateTime localWriteTime = File.GetLastWriteTime(localFilePath);

                if (remoteWriteTime > localWriteTime)
                {
                    bDownload = true;
                }
                else
                {
                    bDownload = false;
                }
            }

            if (bDownload)
            {
                // Download file
                TransferOptions oTrRes = new TransferOptions();

                oTrRes.TransferMode = TransferMode.Automatic; //The Transfer Mode - Automatic, Binary, or Ascii 
                oTrRes.FilePermissions = null;  //Permissions applied to remote files; null for default permissions. Can set user, Group, or other Read/Write/Execute permissions.  
                oTrRes.PreserveTimestamp = false;  //Set last write time of destination file to that of source file - basically change the timestamp to match destination and source files.    
                oTrRes.ResumeSupport.State = TransferResumeSupportState.Off;

                TransferOperationResult transferResult = SFTP_Session.GetFiles(remoteFilePath, localFilePath, false, oTrRes);//.Replace("\\",""));  // I thought this would get files AND folders
                                
                // Throw on any error
                transferResult.Check();

                foreach (TransferEventArgs transfer in transferResult.Transfers)
                {
                    // Store local file info in a data table for processing later
                    ...
                }
                SessionRemoteExceptionCollection srec = transferResult.Failures;

                foreach (SessionRemoteException sre in srec)
                {
                    // Log errors
                }

                // Did the download succeeded?
                if (!transferResult.IsSuccess)
                {
                    // Log error (but continue with other files)
                }
            }
        }
    }

At the end, in local folder I see the files downloaded and copied and subfolders that I created (using above code) but no files in those folders. Can't see what I am missing here.


Solution

  • Your code basically synchronizes a remote directory to a local one.

    Instead of fixing your code, you can simply replace most of it with a simple call to Session.SynchronizeDirectories: https://winscp.net/eng/docs/library_session_synchronizedirectories

    Try the synchronization first in WinSCP GUI to see if it does what you need.

    • If you need to do some processing with the synchronized files, use the SynchronizationResult returned by Session.SynchronizeDirectories. It contains a list of all synchronized files.
    • If you need to exclude some files from the synchronization, use TransferOptions.FileMask.