除非我在退出之前插入睡眠,否则System.Net.FtpClient openwrite不会上传文件

时间:2016-02-22 16:17:04

标签: c# ftps

我正在使用System.Net.FtpClient程序集将文件上传到测试FTP站点。当我运行下面的代码时,文件不会出现在远程位置,除非我使用Thread.Sleep,如下所示(我不想使用):

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);
        }
    }
}

任何人都知道如何在不使用Thread.Sleep的情况下让它工作?我试过冲洗,关闭流,但这没有用。

2 个答案:

答案 0 :(得分:3)

远程FTP服务器与您的代码完全异步。根据服务器的配置,它可能会在文件可用之前执行扫描病毒或其他簿记等操作。除非您可以直接控制FTP服务器,否则您可能无法做任何事情。即便如此,它可能需要进行一些非常深入的配置更改,甚至是不同的软件包。

可能对您有用的一件事是&#34;民意调查&#34;完成上传后的文件。创建一个检查文件的循环,等待1秒,然后重复,直到找到文件或放弃。 Async / Await模式,或来自不同线程的回调,可以帮助您摆脱任何UI冻结,如果它是一个问题。

答案 1 :(得分:2)

感谢Bradley的回答,我设法解决了一个问题:使用FtpClient.OpenWrite(...)上传文件,之后我不断检查服务器上的文件大小,这解决了上传过程中代码执行的问题在它之后执行。

然后我遇到了另一个问题,我设法解决了这个问题:在远程FTP服务器上检查上传文件的文件大小时,你不能总是通过FtpClient.GetFileSize(...)获得文件大小到所有FTP服务器都不支持的FTP命令(SIZE)。解决方案如下: 如果FtpClient.GetFileSize(...)返回大于-1,则SIZE命令可以使用并且可以使用。 如果函数返回-1,则可以遍历服务器上指定文件夹中的文件列表。在那里你得到一个FtpListItem数组,它有一个名为“Size”的属性。 Size返回与FtpClient.GetFileSize(...)实际应该相同的值。

示例代码:

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