Implementering av en FTP-klient i Java
1. Oversikt
I denne opplæringen vil vi se på hvordan vi kan utnytte Apache Commons Net-biblioteket til å samhandle med en ekstern FTP-server.
2. Oppsett
Når du bruker biblioteker som brukes til å samhandle med eksterne systemer, er det ofte lurt å skrive noen ekstra integrasjonstester for å sikre at vi bruker biblioteket riktig.
I dag bruker vi normalt Docker til å spinne opp disse systemene for våre integrasjonstester. Men spesielt når det brukes i passiv modus, er ikke en FTP-server det enkleste programmet å kjøre gjennomsiktig inne i en container hvis vi vil bruke dynamiske porttilordninger (noe som ofte er nødvendig for at tester kan kjøres på en delt CI-server. ).
Derfor bruker vi MockFtpServer i stedet, en Fake / Stub FTP-server skrevet i Java, som gir et omfattende API for enkel bruk i JUnit-tester:
commons-net commons-net 3.6 org.mockftpserver MockFtpServer 2.7.1 test
Det anbefales å alltid bruke den nyeste versjonen. Disse finner du her og her.
3. FTP-støtte i JDK
Overraskende nok er det allerede grunnleggende støtte for FTP i noen JDK-smaker i form av sun.net.www.protocol.ftp.FtpURLConnection.
Vi bør imidlertid ikke bruke denne klassen direkte, og det er i stedet mulig å bruke JDK-ene java.net.URL-klasse som en abstraksjon.
Denne FTP-støtten er veldig grunnleggende, men utnytter de praktiske API-ene til java.nio.file.Files, det kan være nok for enkle brukstilfeller:
@Test offentlig ugyldighet gittRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem () kaster IOException {String ftpUrl = String.format ("ftp: // bruker: [e-postbeskyttet]:% d / foobar.txt", fakeFtpServer.getServerControlPort () URLConnection urlConnection = ny URL (ftpUrl). OpenConnection (); InputStream inputStream = urlConnection.getInputStream (); Files.copy (inputStream, ny fil ("downloaded_buz.txt"). ToPath ()); inputStream.close (); assertThat (ny fil ("downloaded_buz.txt")). eksisterer (); ny fil ("downloaded_buz.txt"). delete (); // rydde opp }
Siden denne grunnleggende FTP-støtten allerede mangler grunnleggende funksjoner som filoppføringer, skal vi bruke FTP-støtte i Apache Net Commons-biblioteket i følgende eksempler.
4. Koble til
Vi må først koble til FTP-serveren. La oss starte med å lage en klasse FtpClient.
Det vil fungere som et abstraksjons-API til den faktiske Apache Commons Net FTP-klienten:
klasse FtpClient {privat streng server; privat int port; privat strengbruker; privat strengpassord; privat FTPClient ftp; // konstruktør ugyldig åpen () kaster IOException {ftp = ny FTPClient (); ftp.addProtocolCommandListener (ny PrintCommandListener (ny PrintWriter (System.out))); ftp.connect (server, port); int svar = ftp.getReplyCode (); hvis (! FTPReply.isPositiveCompletion (svar)) {ftp.disconnect (); kaste ny IOException ("Unntak i å koble til FTP-server"); } ftp.login (bruker, passord); } ugyldig lukk () kaster IOException {ftp.disconnect (); }}
Vi trenger serveradressen og porten, samt brukernavnet og passordet. Etter tilkobling er det nødvendig å faktisk kontrollere svarkoden for å være sikker på at tilkoblingen var vellykket. Vi legger også til en PrintCommandListener, for å skrive ut svarene vi normalt ser når vi kobler til en FTP-server ved hjelp av kommandolinjeverktøy for å stdout.
Siden integrasjonstestene våre vil ha noen kokerplatekoder, som å starte / stoppe MockFtpServer og koble til / koble fra klienten, kan vi gjøre disse tingene @Før og @Etter metoder:
offentlig klasse FtpClientIntegrationTest {private FakeFtpServer fakeFtpServer; private FtpClient ftpClient; @Før offentlig ugyldig oppsett () kaster IOException {fakeFtpServer = ny FakeFtpServer (); fakeFtpServer.addUserAccount (ny UserAccount ("bruker", "passord", "/ data")); FileSystem fileSystem = nytt UnixFakeFileSystem (); fileSystem.add (ny DirectoryEntry ("/ data")); fileSystem.add (ny FileEntry ("/ data / foobar.txt", "abcdef 1234567890")); falskeFtpServer.setFileSystem (fileSystem); falskeFtpServer.setServerControlPort (0); fakeFtpServer.start (); ftpClient = ny FtpClient ("localhost", fakeFtpServer.getServerControlPort (), "bruker", "passord"); ftpClient.open (); } @Etter offentlig annullering av nedbrytning () kaster IOException {ftpClient.close (); fakeFtpServer.stop (); }}
Ved å sette mock-serverkontrollporten til verdien 0, starter vi mock-serveren og en gratis tilfeldig port.
Derfor må vi hente den faktiske porten når vi oppretter FtpClient etter at serveren er startet, bruker fakeFtpServer.getServerControlPort ().
5. Oppføring av filer
Den første faktiske brukssaken vil være å liste opp filer.
La oss starte med testen først, TDD-stil:
@Test offentlig ugyldig givenRemoteFile_whenListingRemoteFiles_thenItIsContainedInList () kaster IOException {Collection files = ftpClient.listFiles (""); assertThat (filer). inneholder ("foobar.txt"); }
Selve implementeringen er like grei. For å gjøre den returnerte datastrukturen litt enklere av hensyn til dette eksemplet, transformerer vi den returnerte FTPFile array blir forvandlet til en liste over Strenger ved hjelp av Java 8 Strømmer:
Collection listFiles (strengbane) kaster IOException {FTPFile [] filer = ftp.listFiles (bane); returner Arrays.stream (filer) .map (FTPFile :: getName) .collect (Collectors.toList ()); }
6. Nedlasting
For å laste ned en fil fra FTP-serveren, definerer vi en API.
Her definerer vi kildefilen og destinasjonen på det lokale filsystemet:
@Test offentlig ugyldighet gittRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem () kaster IOException {ftpClient.downloadFile ("/ buz.txt", "downloaded_buz.txt"); assertThat (ny fil ("downloaded_buz.txt")). eksisterer (); ny fil ("downloaded_buz.txt"). delete (); // rydde opp }
Apache Net Commons FTP-klienten inneholder en praktisk API som vil skrive direkte til en definert OutputStream. Dette betyr at vi kan bruke dette direkte:
void downloadFile (strengkilde, streng destinasjon) kaster IOException {FileOutputStream ut = ny FileOutputStream (destinasjon); ftp.retrieveFile (kilde, ut); }
7. Opplasting
MockFtpServer gir noen nyttige metoder for å få tilgang til innholdet i filsystemet. Vi kan bruke denne funksjonen til å skrive en enkel integrasjonstest for opplastingsfunksjonaliteten:
@Test offentlig ugyldighet gittLocalFile_whenUploadingIt_thenItExistsOnRemoteLocation () kaster URISyntaxException, IOException {File file = new File (getClass (). GetClassLoader (). GetResource ("baz.txt"). ToURI ()); ftpClient.putFileToPath (fil, "/buz.txt"); assertThat (fakeFtpServer.getFileSystem (). eksisterer ("/ buz.txt")). isTrue (); }
Å laste opp en fil fungerer API-vis ganske likt å laste den ned, men i stedet for å bruke en OutputStream, må vi gi en InputStream i stedet:
ugyldig putFileToPath (filfil, strengbane) kaster IOException {ftp.storeFile (bane, ny FileInputStream (fil)); }
8. Konklusjon
Vi har sett at bruk av Java sammen med Apache Net Commons gjør at vi enkelt kan samhandle med en ekstern FTP-server for lese- og skrivetilgang.
Som vanlig er den fullstendige koden for denne artikkelen tilgjengelig i GitHub-arkivet.