Melaksanakan Pelanggan FTP di Java

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat bagaimana memanfaatkan perpustakaan Apache Commons Net untuk berinteraksi dengan pelayan FTP luaran.

2. Persediaan

Semasa menggunakan perpustakaan, yang digunakan untuk berinteraksi dengan sistem luaran, selalunya idea yang baik adalah menulis beberapa ujian integrasi tambahan, untuk memastikan, kami menggunakan perpustakaan dengan betul.

Pada masa ini, kami biasanya menggunakan Docker untuk memacu sistem tersebut untuk ujian integrasi kami. Namun, terutama ketika digunakan dalam mod pasif, pelayan FTP bukanlah aplikasi termudah untuk berjalan dengan telus di dalam wadah jika kita ingin menggunakan pemetaan port dinamis (yang sering diperlukan untuk ujian dapat dijalankan pada pelayan CI bersama ).

Itulah sebabnya kami akan menggunakan MockFtpServer, pelayan Fake / Stub FTP yang ditulis di Java, yang menyediakan API yang luas untuk penggunaan mudah dalam ujian JUnit:

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

Sebaiknya selalu menggunakan versi terkini. Mereka boleh didapati di sini dan di sini.

3. Sokongan FTP di JDK

Anehnya, sudah ada sokongan asas untuk FTP dalam beberapa rasa JDK dalam bentuk sun.net.www.protocol.ftp.FtpURLConnection .

Walau bagaimanapun, kita tidak boleh menggunakan kelas ini secara langsung dan mungkin menggunakan java.net JDK . Kelas URL sebagai pengabstrakan.

Sokongan FTP ini sangat asas, tetapi memanfaatkan kemudahan API java.nio.file.Files, cukup untuk kes penggunaan mudah:

@Test public void givenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem() throws IOException { String ftpUrl = String.format( "ftp://user:[email protected]:%d/foobar.txt", fakeFtpServer.getServerControlPort()); URLConnection urlConnection = new URL(ftpUrl).openConnection(); InputStream inputStream = urlConnection.getInputStream(); Files.copy(inputStream, new File("downloaded_buz.txt").toPath()); inputStream.close(); assertThat(new File("downloaded_buz.txt")).exists(); new File("downloaded_buz.txt").delete(); // cleanup }

Oleh kerana sokongan FTP asas ini sudah kehilangan ciri-ciri asas seperti senarai fail, kami akan menggunakan sokongan FTP di perpustakaan Apache Net Commons dalam contoh berikut.

4. Menyambung

Mula-mula kita perlu menyambung ke pelayan FTP. Mari mulakan dengan membuat kelas FtpClient.

Ia akan berfungsi sebagai API abstraksi kepada klien FTP Net Apache Commons yang sebenarnya:

class FtpClient { private String server; private int port; private String user; private String password; private FTPClient ftp; // constructor void open() throws IOException { ftp = new FTPClient(); ftp.addProtocolCommandListener(new PrintCommandListener(new PrintWriter(System.out))); ftp.connect(server, port); int reply = ftp.getReplyCode(); if (!FTPReply.isPositiveCompletion(reply)) { ftp.disconnect(); throw new IOException("Exception in connecting to FTP Server"); } ftp.login(user, password); } void close() throws IOException { ftp.disconnect(); } }

Kami memerlukan alamat pelayan dan port, serta nama pengguna dan kata laluan. Setelah menyambung perlu memeriksa kod balasan, untuk memastikan penyambungan berjaya. Kami juga menambahkan PrintCommandListener , untuk mencetak respons yang biasanya kami lihat ketika menyambung ke pelayan FTP menggunakan alat baris perintah untuk stdout.

Oleh kerana ujian integrasi kami akan mempunyai beberapa kod boilerplate, seperti memulakan / menghentikan MockFtpServer dan menyambung / memutuskan sambungan pelanggan kami, kami dapat melakukan perkara-perkara ini dalam kaedah @Sebelum dan @Setelah :

public class FtpClientIntegrationTest { private FakeFtpServer fakeFtpServer; private FtpClient ftpClient; @Before public void setup() throws IOException { fakeFtpServer = new FakeFtpServer(); fakeFtpServer.addUserAccount(new UserAccount("user", "password", "/data")); FileSystem fileSystem = new UnixFakeFileSystem(); fileSystem.add(new DirectoryEntry("/data")); fileSystem.add(new FileEntry("/data/foobar.txt", "abcdef 1234567890")); fakeFtpServer.setFileSystem(fileSystem); fakeFtpServer.setServerControlPort(0); fakeFtpServer.start(); ftpClient = new FtpClient("localhost", fakeFtpServer.getServerControlPort(), "user", "password"); ftpClient.open(); } @After public void teardown() throws IOException { ftpClient.close(); fakeFtpServer.stop(); } }

Dengan menetapkan port kawalan pelayan tiruan ke nilai 0, kita memulakan pelayan tiruan dan port rawak percuma.

Itulah sebabnya mengapa kita perlu mendapatkan semula port yang sebenarnya semasa membuat FtpClient setelah pelayan dimulakan, menggunakan fakeFtpServer.getServerControlPort () .

5. Menyenaraikan Fail

Kes penggunaan sebenar pertama adalah menyenaraikan fail.

Mari mulakan dengan ujian pertama, gaya TDD:

@Test public void givenRemoteFile_whenListingRemoteFiles_thenItIsContainedInList() throws IOException { Collection files = ftpClient.listFiles(""); assertThat(files).contains("foobar.txt"); }

Pelaksanaannya sendiri sangat mudah. Untuk membuat data yang dikembalikan menstrukturkan bit yang lebih mudah demi contoh ini, kita mengubah kembali FTPFile mudah berubah menjadi senarai Strings menggunakan Java 8 Streams:

Collection listFiles(String path) throws IOException { FTPFile[] files = ftp.listFiles(path); return Arrays.stream(files) .map(FTPFile::getName) .collect(Collectors.toList()); }

6. Memuat turun

Untuk memuat turun fail dari pelayan FTP, kami menentukan API.

Di sini kita menentukan fail sumber dan tujuan pada sistem fail tempatan:

@Test public void givenRemoteFile_whenDownloading_thenItIsOnTheLocalFilesystem() throws IOException { ftpClient.downloadFile("/buz.txt", "downloaded_buz.txt"); assertThat(new File("downloaded_buz.txt")).exists(); new File("downloaded_buz.txt").delete(); // cleanup }

Klien FTP Apache Net Commons mengandungi API yang mudah, yang akan langsung menulis ke OutputStream yang ditentukan . Ini bermaksud kita boleh menggunakannya secara langsung:

void downloadFile(String source, String destination) throws IOException { FileOutputStream out = new FileOutputStream(destination); ftp.retrieveFile(source, out); }

7. Memuat naik

MockFtpServer menyediakan beberapa kaedah yang berguna untuk mengakses kandungan sistem failnya. Kami boleh menggunakan ciri ini untuk menulis ujian integrasi mudah untuk fungsi memuat naik:

@Test public void givenLocalFile_whenUploadingIt_thenItExistsOnRemoteLocation() throws URISyntaxException, IOException { File file = new File(getClass().getClassLoader().getResource("baz.txt").toURI()); ftpClient.putFileToPath(file, "/buz.txt"); assertThat(fakeFtpServer.getFileSystem().exists("/buz.txt")).isTrue(); }

Memuat naik fail berfungsi seperti API yang serupa dengan memuat turunnya, tetapi bukannya menggunakan OutputStream , kita perlu menyediakan InputStream sebagai gantinya:

void putFileToPath(File file, String path) throws IOException { ftp.storeFile(path, new FileInputStream(file)); }

8. Kesimpulannya

Kita telah melihat, bahawa menggunakan Java bersama dengan Apache Net Commons memungkinkan kita, untuk berinteraksi dengan mudah dengan pelayan FTP luaran, untuk akses membaca dan menulis.

Seperti biasa, kod lengkap untuk artikel ini terdapat di repositori GitHub kami.