Implementacija FTP-klijenta u Javi

1. Pregled

U ovom ćemo uputstvu pogledati kako iskoristiti biblioteku Apache Commons Net za interakciju s vanjskim FTP poslužiteljem.

2. Postavljanje

Kada upotrebljavate knjižnice koje se koriste za interakciju s vanjskim sustavima, često je dobra ideja napisati neke dodatne testove integracije kako bismo bili sigurni da knjižnicu koristimo ispravno.

U današnje vrijeme obično koristimo Docker za okretanje tih sustava za naše integracijske testove. Međutim, posebno kada se koristi u pasivnom načinu rada, FTP poslužitelj nije najlakša aplikacija za transparentno pokretanje unutar spremnika ako želimo koristiti dinamička preslikavanja priključaka (što je često potrebno za testiranje na zajedničkom CI poslužitelju ).

Zbog toga ćemo umjesto toga koristiti MockFtpServer, lažni / zakrčeni FTP poslužitelj napisan na Javi, koji pruža opsežni API za jednostavnu upotrebu u JUnit testovima:

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

Preporučuje se uvijek koristiti najnoviju verziju. Oni se mogu naći ovdje i ovdje.

3. FTP podrška u JDK

Iznenađujuće, već postoji osnovna podrška za FTP u nekim JDK okusima u obliku sun.net.www.protocol.ftp.FtpURLConnection.

Međutim, ne bismo trebali koristiti ovu klasu izravno i umjesto toga je moguće koristiti JDK-ove java.net.Klasa URL-a kao apstrakcija.

Ova FTP podrška je vrlo osnovna, ali iskorištavajući praktične API-je za java.nio.file.Files, to bi moglo biti dovoljno za jednostavne slučajeve upotrebe:

@Test javna praznina givenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem () baca IOException {String ftpUrl = String.format ("ftp: // user: [email protected]:% d / foobar.txt", fakeFtpSerrol.getSortverControl.getServerCoget.ortPort URLConnection urlConnection = novi URL (ftpUrl) .openConnection (); InputStream inputStream = urlConnection.getInputStream (); Files.copy (inputStream, nova datoteka ("downloaded_buz.txt"). ToPath ()); inputStream.close (); assertThat (nova datoteka ("downloaded_buz.txt")). postoji (); nova datoteka ("downloaded_buz.txt"). delete (); // počistiti }

Budući da ovoj osnovnoj FTP podršci već nedostaju osnovne značajke poput popisa datoteka, koristit ćemo FTP podršku u biblioteci Apache Net Commons u sljedećim primjerima.

4. Povezivanje

Prvo se moramo povezati s FTP poslužiteljem. Krenimo s izradom klase FtpClient.

Služit će kao API za apstrakciju stvarnom Apache Commons Net FTP klijentu:

klasa FtpClient {privatni niz poslužitelja; privatna int luka; privatni korisnik niza; privatna lozinka za niz; privatni FTPClient ftp; // konstruktor void open () baca IOException {ftp = novi FTPClient (); ftp.addProtocolCommandListener (novi PrintCommandListener (novi PrintWriter (System.out))); ftp.connect (poslužitelj, port); int odgovor = ftp.getReplyCode (); if (! FTPReply.isPositiveCompletion (odgovor)) {ftp.disconnect (); baciti novi IOException ("Iznimka u povezivanju s FTP poslužiteljem"); } ftp.login (korisnik, lozinka); } void close () baca IOException {ftp.disconnect (); }}

Trebamo adresu poslužitelja i priključak, kao i korisničko ime i lozinku. Nakon povezivanja potrebno je zapravo provjeriti kod za odgovor kako biste bili sigurni da je povezivanje uspješno. Također dodajemo a PrintCommandListener, za ispis odgovora koje bismo obično vidjeli pri povezivanju s FTP poslužiteljem pomoću alata naredbenog retka za stdout.

Budući da će naši integracijski testovi imati neki standardni obrazac, poput pokretanja / zaustavljanja MockFtpServera i povezivanja / odspajanja našeg klijenta, to možemo učiniti u @Prije i @Nakon metode:

javna klasa FtpClientIntegrationTest {private FakeFtpServer fakeFtpServer; privatni FtpClient ftpClient; @Prije javne void setup () baca IOException {fakeFtpServer = new FakeFtpServer (); fakeFtpServer.addUserAccount (novi UserAccount ("korisnik", "lozinka", "/ podaci")); FileSystem fileSystem = novi UnixFakeFileSystem (); fileSystem.add (novi DirectoryEntry ("/ podaci")); fileSystem.add (novi FileEntry ("/ data / foobar.txt", "abcdef 1234567890")); fakeFtpServer.setFileSystem (datotečni sustav); fakeFtpServer.setServerControlPort (0); fakeFtpServer.start (); ftpClient = novi FtpClient ("localhost", fakeFtpServer.getServerControlPort (), "user", "password"); ftpClient.open (); } @Nakon javnog void teardown () baca IOException {ftpClient.close (); fakeFtpServer.stop (); }}

Postavljanjem kontrolnog porta lažnog poslužitelja na vrijednost 0, započinjemo lažni poslužitelj i besplatni slučajni port.

Zato prilikom stvaranja datoteke moramo dohvatiti stvarni port FtpClient nakon pokretanja poslužitelja, pomoću fakeFtpServer.getServerControlPort ().

5. Popis datoteka

Prvi stvarni slučaj upotrebe bit će popisivanje datoteka.

Krenimo prvo s testom, u TDD stilu:

@Test javna praznina givenRemoteFile_whenListingRemoteFiles_thenItIsConintedInList () baca IOException {Datoteke zbirke = ftpClient.listFiles (""); assertThat (datoteke) .contains ("foobar.txt"); }

Sama provedba je jednako jednostavna. Da bi vraćena struktura podataka bila malo jednostavnija zbog ovog primjera, transformiramo vraćeni FTPFile niz se pretvara u popis Žice koristeći Javu 8 Potoci:

Zbirka listFiles (put niza) baca IOException {FTPFile [] files = ftp.listFiles (put); vratiti Arrays.stream (datoteke) .map (FTPFile :: getName) .collect (Collectors.toList ()); }

6. Preuzimanje

Za preuzimanje datoteke s FTP poslužitelja definiramo API.

Ovdje definiramo izvornu datoteku i odredište na lokalnom datotečnom sustavu:

@Test javna praznina givenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem () baca IOException {ftpClient.downloadFile ("/ buz.txt", "downloaded_buz.txt"); assertThat (nova datoteka ("downloaded_buz.txt")). postoji (); nova datoteka ("downloaded_buz.txt"). delete (); // počistiti }

FTP klijent Apache Net Commons sadrži prikladan API, koji će izravno pisati na definirani Izlazni tok. To znači da ovo možemo koristiti izravno:

void downloadFile (izvor niza, odredište niza) baca IOException {FileOutputStream out = novo FileOutputStream (odredište); ftp.retrieveFile (izvor, izlaz); }

7. Učitavanje

MockFtpServer pruža neke korisne metode za pristup sadržaju svog datotečnog sustava. Ovu značajku možemo koristiti za pisanje jednostavnog testa integracije za funkciju prijenosa:

@Test public void givenLocalFile_whenUploadingIt_thenItExistsOnRemoteLocation () baca URISyntaxException, IOException {File file = new File (getClass (). GetClassLoader (). GetResource ("baz.txt"). ftpClient.putFileToPath (datoteka, "/buz.txt"); assertThat (fakeFtpServer.getFileSystem (). postoji ("/ buz.txt")). isTrue (); }

Učitavanje datoteke djeluje po API-ju vrlo slično preuzimanju, ali umjesto upotrebe Izlazni tok, moramo pružiti InputStream umjesto toga:

void putFileToPath (datoteka datoteke, put niza) baca IOException {ftp.storeFile (put, novi FileInputStream (datoteka)); }

8. Zaključak

Vidjeli smo da nam korištenje Jave zajedno s Apache Net Commons omogućuje jednostavnu interakciju s vanjskim FTP poslužiteljem za pristup čitanja i pisanja.

Kao i obično, cjeloviti kôd za ovaj članak dostupan je u našem GitHub spremištu.