FTP-Operationen
Um FTP-Funktionen in Java zu realisieren, bietet es sich der Einfachheit halber an, auf die Bibliothek Apache Commons Net zurückzugreifen. Sie kapselt eine Vielzahl an FTP-Funktionen in Methoden. Vor der Verwendung der Bibliothek muss diese in den Classpath eingebunden werden.
Da beim Aufbau und der Nutzung einer FTP-Verbindung eine
Reihe potentieller Fehler auftreten kann, müssen
die meisten Routinen in try-catch-Blöcke gebettet
werden. Hieraus kann, durch das Abfangen der Fehler,
eine große Menge redundanter Code resultieren, der
dadurch umgangen werden soll, dass die einzelnen
Demonstrationsbeispiele in Form von Methoden direkt in main()
eingebunden werden.
Diese Beispielmethoden werden
dort innerhalb eines try-catch-Blockes aufgerufen und
reichen (durch die throws
-Erweiterung der
Methodensignatur) die jeweilige Ausnahmebehandlung nach
'oben' zur aufrufenden main()
-Methode
weiter.
Dort werden auch die Variablen der
Verbindungsdaten deklariert, die dann als Parameter an
die jeweilige Methode weitergegeben werden.
public class FTPUpAndDownload { public static void main(String[] args) { String host = "javabeginners.de"; String user = "Benutzer"; String pw = "Passwort"; try { // hier Methodenaufruf } catch (IOException e) { e.printStackTrace(); } } }
FTP-Status auslesen
Für die Nutzung grundlegender Client-seitiger
Funktionen stellt die Klasse org.apache.commons.net.ftp.FTPClient
die zentrale dar. Von SocketClient
wird die
Methode connect()
geerbt, die durch
Übergabe des Servernamens die Verbindung herstellt.
Die Methode ist mehrfach überladen und kann
wahlweise mit oder ohne Portangabe als zweitem Parameter
und/oder anstatt eines Host-Strings auch mit einem
Objekt vom Typ InetAdress
aufgerufen
werden.
Nach dem Versuch des Verbindungsaufbaus
sendet der FTP-Server einen entsprechenden Code und ggf.
eine Textmitteilung. Beides kann durch die Methoden getReplyCode()
bzw. getReplyString()
abgefragt werden.
Beginnt der dreistellige Code mit einer 2, ist die
Verbindung erfolgreich zustande gekommen. Dies kann
durch die statische Methode isPositiveCompletion()
der Klasse ReplyCode
abgefragt werden [6].
private static void getFtpStatus(String host) throws IOException { FTPClient ftp = new FTPClient(); ftp.connect(host); String s = ftp.getReplyString(); int code = ftp.getReplyCode(); if (FTPReply.isPositiveCompletion(code)) { String stat = ftp.getStatus(); System.out.println(s); System.out.println("Code: " + code); System.out.println(stat); } else { System.out.println("Verbindung nicht erfolgreich, Replycode: " + code); } ftp.disconnect(); }
Die Ausgabe sieht wie folgt aus:
220---------- Welcome to Pure-FTPd [privsep] [TLS] ---------- 220-You are user number 1 of 50 allowed. 220-Local time is now 11:52. Server port: 21. 220-This is a private system - No anonymous login 220-IPv6 connections are also welcome on this server. 220 You will be disconnected after 15 minutes of inactivity. Code: 220 null
Hier zeigt sich, dass der Status des Servers offensichtlich nicht auslesbar ist. Dies ändert sich, nachdem sich ein authorisierter User angemeldet hat:
private static void getFtpStatus(String host, String user, String pw) throws IOException { FTPClient ftp = new FTPClient(); ftp.connect(host); ftp.login(user, pw); String s = ftp.getReplyString(); int code = ftp.getReplyCode(); if (FTPReply.isPositiveCompletion(code)) { String stat = ftp.getStatus(); System.out.println(s); System.out.println("Code: " + code); System.out.println(stat); } else { System.out.println("Verbindung nicht erfolgreich, Replycode: " + code); } ftp.logout(); ftp.disconnect(); }
Die Ausgabe ändert sich wie folgt:
230 OK. Current restricted directory is / Code: 230 211 https://www.pureftpd.org/
Navigation in der Verzeichnishierarchie
Für die Navigation in der entfernten
Verzeichnishierarchie werden im Wesentlichen die
Methoden changeWorkingDirectory(),
changeToParentDirectory()
und printWorkingDirectory()
der Klasse FTPClient
verwendet. Im Beispiel
wird dies anhand der Methode listFiles()
demonstriert.
In ihr loggt sich der FTP-Benutzer ein
und wechselt in das als Parameter übergebene
Verzeichnis. Nach Auflistung und Ausgabe der dort
vorhandenen Datei- und Verzeichnis-Pfade wird erst in
das Elternverzeichnis und dann wiederum in ein weiter
unten gelegenens Verzeichnis gewechselt.
Ohne gesonderte Pfadangabe befindet sich der angemeldete
FTP-User üblicherweise im Root-Verzeichnis des ihm
zugänglichen Bereiches des FTP-Servers. Um das
Arbeitsverzeichnis zu wechseln, wird der absolute
Zielpfad1 als Parameter an
changeWorkingDirectory()
übergeben
[8]. Die dort gelisteten Verzeichnisse und Dateien
werden dann durch die Methode listNames()
ermittelt. Sie liefert die zugehörigen Pfade. Deren
Ausgabe findet im Beispiel durch die Hilfsmethode printFileNames()
statt [10, 27].
In das übergeordnete Verzeichnis
gelangt man durch changeToParentDirectory()
[11], und um den Pfad des aktuellen
Arbeitsverzeichnisses zu ermitteln, kann printWorkingDirectory()
verwendet werden [9, 12].
private static void listFiles(String host, String user, String pw, String remotePath) throws IOException { String[] fileNames = null; FTPClient ftp = null; ftp = new FTPClient(); ftp.connect(host); if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { if (ftp.login(user, pw)) { ftp.changeWorkingDirectory(remotePath); fileNames = ftp.listNames(ftp.printWorkingDirectory()); printFileNames(fileNames); if (ftp.changeToParentDirectory()) { fileNames = ftp.listNames(ftp.printWorkingDirectory()); printFileNames(fileNames); if (ftp.changeWorkingDirectory(ftp.printWorkingDirectory() + "/Swing-Elemente/Tabellen")) { fileNames = ftp.listNames(ftp.printWorkingDirectory()); printFileNames(fileNames); } } if (ftp.logout()) { System.out.println("Logout erfolgreich"); } } } ftp.disconnect(); } private static void printFileNames(String[] fileNames) { if (fileNames == null) return; for (String s : fileNames) { System.out.println(s); } System.out.println(); }
Dateigröße und -typ ermitteln
Im folgenden Beispiel wird durch die o.a. Methode FTPClient#listFiles()
ein Array des Typs FTPFile
erzeugt [8]. Es
enthält die Datei- und Verzeichnisobjekte des durch
den Parameter remotePath
übergebenen
Pfades. In einer Schleife wird das Array durchlaufen und
für jedes Objekt dessenn Typ abgefragt [17]. FTPFile
definiert eine Reihe an int-Konstanten, die nachfolgend
innerhalb einer switch-case-Verzweigung verwendet
werden, um bei den ermittelten Typen zwischen Dateien,
Verzeichnissen und Symbolischen Links zu differenzieren.
Abschließend
erfolgt die Ausgabe des Namens unter Angabe des Typs und
der Größe in byte auf die Konsole.
private static void listFileSizes(String host, String user, String pw, String remotePath) throws IOException { FTPFile[] files = null; FTPClient ftp = null; ftp = new FTPClient(); ftp.connect(host); if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { if (ftp.login(user, pw)) { files = ftp.listFiles(remotePath); } } ftp.disconnect(); if (files == null) return; int type; String s; for (FTPFile f : files) { type = f.getType(); switch (type) { case FTPFile.FILE_TYPE: s = "(Datei)"; break; case FTPFile.DIRECTORY_TYPE: s = "(Verzeichnis)"; break; case FTPFile.SYMBOLIC_LINK_TYPE: s = "(Symbolischer Link)"; break; default: s = "(Unbekannter Typ)"; } System.out.println(f.getName() + " " + s + ": " + f.getSize() + " bytes"); } }
Download
Um einen Download vom FTP-Server durchzuführen,
werden der Methode loadDown()
neben dem
Hostnamen, dem Nutzernamen und seinem Passwort noch
jeweils ein Pfad zur lokalen und zur entfernten Datei
übergeben. Beide Dateien müssen existieren.
Auf die Gültigkeitsprüfung wird hier jedoch
aus Gründen der Übersichtlichkeit verzichtet.
Mittels
Übergabe des lokalen Dateinamens an den Konstruktor
wird ein FileOutputStream
erzeugt [9].
Dieser wird gemeinsam mit dem entfernten Pfad der
Methode retrieveFile()
als Parameter
übergeben [10]. Die Methode gibt nach erfolgreicher
Übertragung true
zurück, sodass
die Existenz der heruntergeladenen Datei nach Erzeugung
eines File
-Objektes überprüft
werden kann [12].
private static void loadDown(String host, String user, String pw, String remoteFile, String localFile) throws IOException { FTPClient ftp = null; ftp = new FTPClient(); ftp.connect(host); if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { if (ftp.login(user, pw)) { System.out.println("Login erfolgreich"); FileOutputStream out = new FileOutputStream(localFile); if (ftp.retrieveFile(remoteFile, out)) { File f = new File(localFile); if (f.exists() && f.length() > 0) { System.out.println("Download erfolgreich: " + f.getName() + ", " + f.length() + " bytes"); } } if (ftp.logout()) { System.out.println("Logout erfolgreich"); } } } }
Upload
Der Upload auf einen FTP-Server ähnelt hinsichtlich
seiner Durchführung stark dem Download. Statt eines
FileOutputStream
wird hier jedoch ein FileInputStream
gebildet, der anschließend gemeinsam mit dem
entfernten Dateipfad an die Methode storeFile()
des FTPClient
-Objektes übergeben wird.
private static void loadUp(String host, String user, String pw, String remoteFile, String localFile) throws IOException { FTPClient ftp = null; ftp = new FTPClient(); ftp.connect(host); if (FTPReply.isPositiveCompletion(ftp.getReplyCode())) { if (ftp.login(user, pw)) { InputStream in = new FileInputStream(localFile); if (ftp.storeFile(remoteFile, in)) { System.out.println("Upload erfolgreich: " + localFile + " -> " + remoteFile); } if (ftp.logout()) { System.out.println("Logout erfolgreich"); } } } }
1) Hier ist der absolute Pfad des FTP-Servers gemeint: Liegen die virtuellen Hosts auf einem Server z.B. unter /var/www/ und dort das Verzeichnis javabeginners als Host, so ist dies oft auch das Wurzelverzeichnis des FTP-Servers. Befindet sich dort das Verzeichnis Swing-Elemente im Verzeichnis Swing, so lautet der absolute Pfad /Swing/Swing-Elemente.
Wenn Ihnen javabeginners.de gefällt, freue ich mich über eine Spende an diese gemeinnützigen Organisationen.