System.Net.FtpClient openwrite does not load the file unless I insert a sleep before logging out

I use the System.Net.FtpClient assembly to upload the file to a test FTP site. When I run the code below, the file does not appear in the remote location unless I use Thread.Sleep as described below (which I would prefer not to use):

using System; using System.IO; using System.Net; using System.Net.FtpClient; using System.Security.Cryptography.X509Certificates; using System.Threading; namespace FtpsUploadTest { /// <summary> /// The ftp publisher. /// </summary> public class FtpPublisher { private readonly FtpsSettings _settings; private readonly IFtpClient _ftpClient; /// <summary> /// Initializes a new instance of the <see cref="FtpPublisher"/> class. /// </summary> public FtpPublisher() { _ftpClient = new FtpClient(); _settings = SettingsReader.GetMySettings(); Init(); } /// <summary> /// The publish. /// </summary> /// <param name="fileToUpload"> /// The input file path. /// </param> public void Publish(string fileToUpload) { var remoteFileName = Path.GetFileName(fileToUpload); Console.WriteLine("FTPS host: {0} remote path: {1}", _settings.FtpsRemoteHost, _settings.FtpsRemotePath); if (!_ftpClient.IsConnected) { _ftpClient.Connect(); } var fullRemotePath = string.Format("{0}/{1}", _settings.FtpsRemotePath, remoteFileName); using (var ftpStream = _ftpClient.OpenWrite(fullRemotePath)) using (var inputStream = new FileStream(fileToUpload, FileMode.Open)) { inputStream.CopyTo(ftpStream); Thread.Sleep(5000); // <------------------- DOESNT WORK IF REMOVE THIS SLEEP!! } Console.WriteLine("File '{0}' published successfully", fileToUpload); } private void Init() { _ftpClient.Host = _settings.FtpsRemoteHost; _ftpClient.Port = _settings.FtpsRemotePort; _ftpClient.DataConnectionConnectTimeout = 60000; _ftpClient.ConnectTimeout = 60000; _ftpClient.Credentials = new NetworkCredential(_settings.FtpsUserId, string.Empty); _ftpClient.DataConnectionType = 0; if (string.IsNullOrEmpty(_settings.CertFile) || string.IsNullOrEmpty(_settings.CertPassword)) { return; } _ftpClient.ClientCertificates.Add(CreateCertificate(_settings.CertFile, _settings.CertPassword)); _ftpClient.EncryptionMode = (FtpEncryptionMode)2; _ftpClient.DataConnectionEncryption = true; } private X509Certificate CreateCertificate(string certFile, string certPassword) { return new X509Certificate(certFile, certPassword, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet); } } } 

Does anyone know how I can make it work without using Thread.Sleep? I tried flushing, closing the streams, but this does not help.

+5
source share
2 answers

The remote FTP server works completely asynchronously from your code. Depending on the server’s configuration, it can do things like scan for viruses or another account before it makes a file available. Perhaps you can not do anything about it if you do not have direct control of the FTP server. Even then, this may require some fairly deep configuration changes or even another software package.

One thing that might work for you is to β€œpoll” the file after the download is complete. Make a loop that checks the file, waits 1 second, then repeats until it finds the file or surrenders. An Async / Await template or a callback from another thread can help you get rid of any UI hangs from this if this is a problem.

+3
source

Thanks to Bradley's answer, I managed a workaround: upload the file using FtpClient.OpenWrite (...), after which I constantly check the file size on the server, which solved the problem of code execution during the download process, which should be executed after it.

Then I came across another problem that I managed to work around: when checking the file size of the uploaded file on a remote FTP server, you cannot always get the file size via FtpClient.GetFileSize (...) to the FTP command (SIZE), which does not supported by all FTP servers. The solution was as follows: If FtpClient.GetFileSize (...) returns greater than -1, the SIZE command works and can be used. If the function returns -1, you can simply traverse the list of files in the specified folder on the server. There you get an FtpListItem array that has the Size property. Size returns the same value as FtpClient.GetFileSize (...) actually.

Code example:

 const string folderPath = "/media/"; string fileName = "image.png"; string fullRemotePath = folderPath + fileName; string fileToUpload = @"C:\image.png"; FtpClient ftpClient = new FtpClient(); // start FtpClient connection etc FileInfo fileInfo = new FileInfo(fileToUpload); long actualFileSize = fileInfo.Length; using (var ftpStream = ftpClient.OpenWrite(fullRemotePath)) { using (var inputStream = new FileStream(fileToUpload, FileMode.Open)) { inputStream.CopyTo(ftpStream); //Thread.Sleep(5000); <-- not necessary anymore } } long fileSizeOnFtp = ftpClient.GetFileSize(fullRemotePath); while (fileSizeOnFtp < actualFileSize) { if (fileSizeOnFtp > -1) { fileSizeOnFtp = ftpClient.GetFileSize(fullRemotePath); } else { var ftpListItem = ftpClient.GetListing(folderPath).Where(x => x.Name == fileName).FirstOrDefault(); if (ftpListItem != null) { fileSizeOnFtp = ftpListItem.Size; } else { // the program only could run into this issue if the folder couldn't be listed throw new Exception(); } } } // execute everything else after ftp upload has finished 
+1
source

Source: https://habr.com/ru/post/1243593/


All Articles