Implementeren van een FTP-Client in Java

1. Overzicht

In deze tutorial zullen we bekijken hoe we de Apache Commons Net-bibliotheek kunnen gebruiken om te communiceren met een externe FTP-server.

2. Installatie

Wanneer u bibliotheken gebruikt die worden gebruikt om te communiceren met externe systemen, is het vaak een goed idee om enkele aanvullende integratietests te schrijven om er zeker van te zijn dat we de bibliotheek correct gebruiken.

Tegenwoordig gebruiken we Docker normaal gesproken om die systemen op te starten voor onze integratietests. Maar vooral als het in passieve modus wordt gebruikt, is een FTP-server niet de gemakkelijkste applicatie om transparant in een container te draaien als we gebruik willen maken van dynamische poorttoewijzingen (wat vaak nodig is om tests uit te kunnen voeren op een gedeelde CI-server). ).

Daarom gebruiken we in plaats daarvan MockFtpServer, een Fake / Stub FTP-server geschreven in Java, die een uitgebreide API biedt voor eenvoudig gebruik in JUnit-tests:

 commons-net commons-net 3.6 org.mockftpserver MockFtpServer 2.7.1 test 

Het wordt aanbevolen om altijd de laatste versie te gebruiken. Die zijn hier en hier te vinden.

3. FTP-ondersteuning in JDK

Verrassend genoeg is er al basisondersteuning voor FTP in sommige JDK-smaken in de vorm van sun.net. www.protocol.ftp.FtpURLConnection.

We zouden deze klasse echter niet rechtstreeks moeten gebruiken en in plaats daarvan is het mogelijk om de JDK's te gebruiken java.net.URL-klasse als abstractie.

Deze FTP-ondersteuning is erg basic, maar maakt gebruik van de handige API's van java.nio.file.Files, het zou voldoende kunnen zijn voor eenvoudige gebruiksscenario's:

@Test openbare leegte gegevenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem () gooit IOException {String ftpUrl = String.format ("ftp: // gebruiker: [e-mail beveiligd]:% d / foobar.txt", nepFtpServer.getServerControlPort ()); URLConnection urlConnection = nieuwe URL (ftpUrl) .openConnection (); InputStream inputStream = urlConnection.getInputStream (); Files.copy (inputStream, nieuw bestand ("gedownload_buz.txt"). ToPath ()); inputStream.close (); assertThat (nieuw bestand ("gedownload_buz.txt")). bestaat (); nieuw bestand ("gedownloade_buz.txt"). delete (); // schoonmaken }

Aangezien deze basis-FTP-ondersteuning al basisfuncties zoals bestandsvermeldingen mist, gaan we FTP-ondersteuning gebruiken in de Apache Net Commons-bibliotheek in de volgende voorbeelden.

4. Verbinden

We moeten eerst verbinding maken met de FTP-server. Laten we beginnen met het maken van een klas FtpClient.

Het zal dienen als een abstractie-API voor de daadwerkelijke Apache Commons Net FTP-client:

klasse FtpClient {privé String-server; privé int poort; privé String-gebruiker; privé String-wachtwoord; privé FTPClient ftp; // constructor void open () gooit IOException {ftp = new FTPClient (); ftp.addProtocolCommandListener (nieuwe PrintCommandListener (nieuwe PrintWriter (System.out))); ftp.connect (server, poort); int antwoord = ftp.getReplyCode (); if (! FTPReply.isPositiveCompletion (antwoord)) {ftp.disconnect (); gooi nieuwe IOException ("Uitzondering bij het verbinden met FTP-server"); } ftp.login (gebruiker, wachtwoord); } void close () gooit IOException {ftp.disconnect (); }}

We hebben het serveradres en de poort nodig, evenals de gebruikersnaam en het wachtwoord. Nadat u verbinding heeft gemaakt, moet u de antwoordcode controleren om er zeker van te zijn dat de verbinding is gelukt. We voegen ook een PrintCommandListener, om de reacties af te drukken die we normaal zouden zien wanneer we verbinding maken met een FTP-server met behulp van opdrachtregelprogramma's naar stdout.

Aangezien onze integratietests enige standaardcode hebben, zoals het starten / stoppen van de MockFtpServer en het verbinden / verbreken van onze client, kunnen we deze dingen doen in de @Voordat en @Na methoden:

openbare klasse FtpClientIntegrationTest {privé FakeFtpServer fakeFtpServer; privé FtpClient ftpClient; @Before public void setup () gooit IOException {fakeFtpServer = nieuwe FakeFtpServer (); fakeFtpServer.addUserAccount (nieuwe UserAccount ("gebruiker", "wachtwoord", "/ data")); FileSystem fileSystem = nieuwe UnixFakeFileSystem (); fileSystem.add (nieuwe DirectoryEntry ("/ data")); fileSystem.add (nieuwe FileEntry ("/ data / foobar.txt", "abcdef 1234567890")); fakeFtpServer.setFileSystem (bestandssysteem); fakeFtpServer.setServerControlPort (0); fakeFtpServer.start (); ftpClient = nieuwe FtpClient ("localhost", nepFtpServer.getServerControlPort (), "gebruiker", "wachtwoord"); ftpClient.open (); } @After public void teardown () gooit IOException {ftpClient.close (); fakeFtpServer.stop (); }}

Door de mock-servercontrolepoort in te stellen op de waarde 0, starten we de mock-server en een vrije willekeurige poort.

Daarom moeten we de daadwerkelijke poort ophalen bij het maken van het FtpClient nadat de server is gestart, gebruikt u nepFtpServer.getServerControlPort ().

5. Lijstbestanden

Het eerste daadwerkelijke gebruik is het opsommen van bestanden.

Laten we eerst beginnen met de test, in TDD-stijl:

@Test openbare leegte gegevenRemoteFile_whenListingRemoteFiles_thenItIsContainedInList () gooit IOException {Collection files = ftpClient.listFiles (""); assertThat (bestanden) .contains ("foobar.txt"); }

De implementatie zelf is al even eenvoudig. Om de geretourneerde datastructuur wat eenvoudiger te maken omwille van dit voorbeeld, transformeren we het geretourneerde FTP-bestand array wordt omgezet in een lijst met Snaren met behulp van Java 8 Stromen:

Verzameling listFiles (String path) genereert IOException {FTPFile [] bestanden = ftp.listFiles (pad); retourneer Arrays.stream (bestanden) .map (FTPFile :: getName) .collect (Collectors.toList ()); }

6. Downloaden

Voor het downloaden van een bestand van de FTP-server definiëren we een API.

Hier definiëren we het bronbestand en de bestemming op het lokale bestandssysteem:

@Test openbare leegte gegevenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem () gooit IOException {ftpClient.downloadFile ("/ buz.txt", "gedownload_buz.txt"); assertThat (nieuw bestand ("gedownload_buz.txt")). bestaat (); nieuw bestand ("gedownloade_buz.txt"). delete (); // schoonmaken }

De Apache Net Commons FTP-client bevat een handige API, die rechtstreeks naar een gedefinieerd OutputStream. Dit betekent dat we dit direct kunnen gebruiken:

void downloadFile (String-bron, String-bestemming) gooit IOException {FileOutputStream out = nieuwe FileOutputStream (bestemming); ftp.retrieveFile (bron, uit); }

7. Uploaden

De MockFtpServer biedt enkele handige methoden om toegang te krijgen tot de inhoud van zijn bestandssysteem. We kunnen deze functie gebruiken om een ​​eenvoudige integratietest te schrijven voor de uploadfunctionaliteit:

@Test openbare leegte gegevenLocalFile_whenUploadingIt_thenItExistsOnRemoteLocation () gooit URISyntaxException, IOException {File file = new File (getClass (). GetClassLoader (). GetResource ("baz.txt"). ToURI ()); ftpClient.putFileToPath (bestand, "/buz.txt"); assertThat (fakeFtpServer.getFileSystem (). bestaat ("/ buz.txt")). isTrue (); }

Het uploaden van een bestand werkt API-gewijs vergelijkbaar met het downloaden ervan, maar in plaats van een OutputStream, moeten we een InputStream in plaats daarvan:

void putFileToPath (File file, String path) gooit IOException {ftp.storeFile (pad, nieuwe FileInputStream (file)); }

8. Conclusie

We hebben gezien dat het gebruik van Java samen met Apache Net Commons ons in staat stelt om gemakkelijk te communiceren met een externe FTP-server, zowel voor lees- als schrijftoegang.

Zoals gewoonlijk is de volledige code voor dit artikel beschikbaar in onze GitHub-repository.