2016-02-22 48 views
5

Ich benutze die System.Net.FtpClient-Assembly, um eine Datei auf eine Test-FTP-Site hochzuladen. Wenn ich den folgenden Code ausführen wird die Datei nicht in der entfernten Stelle angezeigt, wenn ich eine Thread.Sleep nach unten verwenden (was ich würde es vorziehen, nicht zu verwenden):System.Net.FtpClient openwrite lädt die Datei nicht, es sei denn, ich füge einen Ruhezustand ein, bevor ich ihn ausschalte

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

Wer weiß, wie ich bekommen kann funktioniert es ohne Verwendung eines Thread.Sleep? Ich habe versucht, Flushing, Schließen von Streams, aber das hilft nicht.

+0

Haben Sie in Ihrer Anwendung verwenden Threading? Kann dieselbe FtpPublisher-Instanz versehentlich von mehreren Threads gleichzeitig verwendet werden? –

+0

Ist die Datei überhaupt nicht sichtbar, selbst wenn Sie mehrere Sekunden im Debugger warten? –

+0

Joachim, hier wird kein Threading verwendet. Ich habe das Beispiel so einfach wie möglich gemacht und konnte das Problem reproduzieren. – user1229458

Antwort

3

Der Remote-FTP-Server agiert vollständig asynchron von Ihrem Code. Abhängig von der Konfiguration des Servers kann es beispielsweise nach Viren oder anderer Buchführung suchen, bevor die Datei verfügbar gemacht wird. Möglicherweise können Sie nichts dagegen tun, es sei denn, Sie haben direkte Kontrolle über den FTP-Server. Selbst dann werden einige Konfigurationsänderungen oder sogar ein anderes Softwarepaket benötigt.

Eine Sache, die für Sie arbeiten könnte, ist, nach Abschluss des Uploads für die Datei "abzufragen". Machen Sie eine Schleife, die nach der Datei sucht, 1 Sekunde wartet und dann wiederholt, bis sie die Datei findet oder aufgibt. Das Async/Await-Muster oder ein Rückruf von einem anderen Thread kann Ihnen dabei helfen, bei einem Problem alle UI-Einfrierungen zu entfernen.

+0

Danke Ich muss mit dem Besitzer des FTP-Servers überprüfen, ob sie dies bestätigen können. Sie behaupten, sie hätten andere Kunden erfolgreich hochgeladen, aber wenn diese Clients nicht sofort beenden, würden sie nie das Problem bemerken – user1229458

+1

Ich weiß, das ist ein alter Thread, aber für was es wert ist ... Haben Sie jemals eine Antwort zurück auf Ihrem bekommen Ende? Ich lief in das gleiche Problem und Ihre Schlafmethode half tatsächlich. Ich habe einen CrushFTP-Server auf einer Linux-Box in meinem Büro gebaut und das Problem (es scheint) ist mit dem FTP-Server. Wenn Sie die Verbindung zum Server trennen, bevor er auf die Festplatte geschrieben wurde, werden die Puffer gelöscht. So sieht es für mich aus. Bitte lassen Sie mich wissen, wenn Sie etwas entdeckt haben. –

1

Dank Bradleys Antwort habe ich eine Problemumgehung geschafft: Hochladen der Datei mit FtpClient.OpenWrite (...) und danach überprüfe ich ständig die Dateigröße auf dem Server, was das Problem des während des Hochladens ausgeführten Codes löste sollte danach ausgeführt werden.

Dann stieß ich auf ein anderes Problem, für das ich eine Umgehung für: Wenn Sie die Dateigröße der Upload-Datei auf dem Remote-FTP-Server überprüfen, können Sie nicht immer die Dateigröße über FtpClient.GetFileSize (...) aufgrund eines FTP-Befehls (SIZE), der nicht von allen FTP-Servern unterstützt wird. Die Lösung war folgende: Wenn FtpClient.GetFileSize (...) zurückgibt, ist größer als -1, der SIZE-Befehl funktioniert und kann verwandt werden. Wenn die Funktion -1 zurückgibt, können Sie einfach eine Liste von Dateien im angegebenen Ordner auf dem Server durchlaufen. Dort erhalten Sie ein Array von FtpListItem mit einer Eigenschaft namens "Size". Größe gibt den gleichen Wert wie FtpClient.GetFileSize (...) eigentlich zurück.

Beispielcode:

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