System.Net.FtpClient openwrite doesnt upload file unless I insert a sleep before exitting

守給你的承諾、 提交于 2019-12-10 13:23:23

问题


I'm using the System.Net.FtpClient assembly to upload a file to a test FTP site. When I run the below code the file doesn't appear in the remote location unless I use a Thread.Sleep as per below (which I'd 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);
        }
    }
}

Anyone know how I can get it to work without using a Thread.Sleep? I've tried flushing, closing streams but that doesn't help.


回答1:


The remote FTP server is acting completely asynchronously from your code. Depending on the configuration of the server it might do things like scan for viruses or other bookkeeping before it makes the file available. There may be nothing you can do about this unless you have direct control over the FTP server. Even then it might require some pretty in depth configuration changes, or even a different software package.

One thing that might work for you is to "poll" for the file after completing the upload. Make a loop that checks for the file, waits 1 second, then repeats until it finds the file or gives up. The Async / Await pattern, or a call-back from a different thread, can help you get rid of any UI freezes from this if it's a problem.




回答2:


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

Then I ran into another problem which I managed a workaround for: when checking the file size of the uploading file on the remote FTP server, you can't always get the file size via FtpClient.GetFileSize(...) due to a FTP command (SIZE) that's not supported by all FTP servers. The solution was the following: If FtpClient.GetFileSize(...) returns is greater than -1, the SIZE command works and can be used. If the function returns -1, you can just traverse over a list of files in the specified folder on the server. There you get an array of FtpListItem which has a property called "Size". Size return the same value as FtpClient.GetFileSize(...) actually should.

Example Code:

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


来源:https://stackoverflow.com/questions/35558561/system-net-ftpclient-openwrite-doesnt-upload-file-unless-i-insert-a-sleep-before

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!