C# Files auf FTP Server auflisten
-
Hallo
Ich möchte alle Files die sich in einem Ordner auf einem FTP Server befinden auflisten. Dies mache ich mit dem folgenden Sourcecode in C#.
private void getFileList(string FTPAddress, string username, string password) { List<string> files = new List<string>(); try { //Optional this.Text = "Connecting..."; Application.DoEvents(); //Create FTP request FtpWebRequest request = FtpWebRequest.Create(FTPAddress) as FtpWebRequest; request.Method = WebRequestMethods.Ftp.ListDirectory; request.Credentials = new NetworkCredential(username, password); request.UsePassive = true; request.UseBinary = true; request.KeepAlive = false; //Read the server's response this.Text = "Retrieving List..."; Application.DoEvents(); FtpWebResponse response = request.GetResponse() as FtpWebResponse; Stream responseStream = response.GetResponseStream(); StreamReader reader = new StreamReader(responseStream); while (!reader.EndOfStream) { Application.DoEvents(); files.Add(reader.ReadLine()); } //Clean-up reader.Close(); responseStream.Close(); //redundant response.Close(); } catch (Exception) { MessageBox.Show("There was an error connecting to the FTP Server"); } }
Mein Problem der Ordnername kann Leerzeichen enthalten. Wenn dies der Fall ist bekomme ich keinen Inhalt zurück. Woran kann das liegen??
Wie kann ich zwischen Ordnern und Datein unterscheiden??
Viel Dank für die Hilfe
mfg Harald
-
ListDirectory stellt die FTP NLIST-Protokollmethode dar, die eine kurze Auflistung der Dateien auf einem FTP-Server abruft.
Für eine ausführliche Auflistung der Dateien auf einem FTP-Server verwende besser das WebRequestMethods.Ftp.ListDirectoryDetails-Feld.
-
Application.DoEvents(..) sollte nicht verwendet werden, weil damit verschiedene Probleme auftreten. Es gibt im Forum (wenn ich mich recht erinnere) einen guten Thread darüber.
Eine Lösung, damit das GUI nicht "einfriert" wäre das ganze asynchron zu lösen.
(Bsp. mit einem Delegaten und BeginInvoke(..)).
-
Was mir auffällt
- Da ist recht viel verschiedenes in nur einer Methode -> Lagere aus in mehrere kleine Methoden (Wiederverwendbarkeit + Wartbar + Sauberkeit)
- Alles ist in nur einem Try-Catch Block -> Wenn keine Leserechte bestehen bekommt man "Connection error" angezeigt was falsch ist.
- Du fängst Exception aber verwendest es nicht.
- getFileList ist recht unübliches casing in C#.
- Du hast alles in dem UI Thread -> Verpacke es in einem Backgroundworker, dann kannst du ach den Progress angenehmer mitteilen und verzichtest auf diese gruseligen DoEvents().
- Du hast unnötige Kommentare -> Was da passiert sieht man doch, und wenn du eine Methode hast "ReportProgress(x, "text")" und "CreateWebReauest(user, pw)" ist es deutlich genug.
- Du castest sachen mit "as" aber prüfst sie danach nicht -> entweder auf null prüfen oder direkt casten.
- Du hast ein "Redundant" block, wenn du es weißt, warum schmeißt du es nicht raus?
- Du hast keine Ausfallsicherheit für die Streams -> using ist eine gute Abhilfe.
- Du gibst die files liste nicht zurück.Auf Basis deines Codes hab ich mal das hier runter getippt (brauchte mal etwas Abwechselung).
public class ReaderArgs { public string FtpAddress { get; set; } public string UserName { get; set; } public string Password { get; set; } } public class FinishedArgs : EventArgs { public FinishedArgs(List<string> files) { Files = files; } public List<string> Files { get; private set; } } public class ErrorArgs : EventArgs { public ErrorArgs(Exception exception) { Exception = exception; } public Exception Exception { get; private set; } } public class ProgressChangedArgs : EventArgs { public ProgressChangedArgs(string message) { Message = message; } public string Message { get; private set; } }
public class Reader { private BackgroundWorker _reader; public event EventHandler<FinishedArgs> ReadFinish; public event EventHandler<ErrorArgs> ReadError; public event EventHandler<ProgressChangedArgs> ProgressChanged; public Reader() { _reader = new BackgroundWorker(); _reader.WorkerReportsProgress = true; _reader.DoWork += new DoWorkEventHandler(Reader_DoWork); _reader.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Reader_RunWorkerCompleted); _reader.ProgressChanged += new ProgressChangedEventHandler(Reader_ProgressChanged); } public void Run(ReaderArgs args) { _reader.RunWorkerAsync(args); } private void Reader_DoWork(object sender, DoWorkEventArgs e) { ReaderArgs args = (ReaderArgs)e.Argument; _reader.ReportProgress(0, "Connecting..."); FtpWebRequest request = CreateWebRequest(args); _reader.ReportProgress(0, "Retrieving List..."); FtpWebResponse response = (FtpWebResponse)request.GetResponse(); e.Result = ReadResponseTexts(response); } private FtpWebRequest CreateWebRequest(ReaderArgs args) { FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(args.FtpAddress); request.Method = WebRequestMethods.Ftp.ListDirectory; request.Credentials = new NetworkCredential(args.UserName, args.Password); request.UsePassive = true; request.UseBinary = true; request.KeepAlive = false; return request; } private List<string> ReadResponseTexts(FtpWebResponse response) { var files = new List<string>(); using (StreamReader reader = new StreamReader(response.GetResponseStream())) { while (!reader.EndOfStream) files.Add(reader.ReadLine()); } return files; } private void Reader_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { if (e.Error != null) { var handler = ReadError; if (handler != null) handler(this, new ErrorArgs(e.Error)); } else { var handler = ReadFinish; if (handler != null) handler(this, new FinishedArgs((List<string>)e.Result)); } } private void Reader_ProgressChanged(object sender, ProgressChangedEventArgs e) { var handler = ProgressChanged; if (handler != null) handler(this, new ProgressChangedArgs(e.UserState.ToString())); } }
Der Aufrufer des Readers muss nur die Events abonnieren, ReadError ist zur Anzeige eines Fehlers, ProgressChanged ist zur anzeige des State in einer ListBox oder Label, und ReadFinish beinhaltet die gelesenen Dateien.
Nachdem er die Events abonniert hat, muss er nur noch Run aufrufen, schon läuft der Vorgang in einem eigenen Worker Thread(Der Code ist ungetestet getippt, muss so nicht funktionieren)
-
Hi,
ich muß jetzt aber wirklich mal impulsiv behaupten dass der Originalcode in seiner Kompaktheit leichter zu lesen und zu verstehen ist als der von CSL gepostete Code, auch wenn er eleganter,'aufgeräumter' und fehlerfrei(er) ist.
-
Der original Code ist immer noch da, nur eben in 3 Methoden aufgeteilt.
Das drum herum ist nunmal notwendig wenn man es nach einem Worker thread schiebt. Schau mal genau drüber, da ist nirgendwo Hexenwerk.Wenn seine Applikation am ende noch mehr macht außer Dateien anzeigen, braucht man eh mehr Architektur als nur eine Methode pro Aktion mit Tonnen von Code Duplikation.