Menerapkan Klien FTP di Java

1. Ikhtisar

Dalam tutorial ini, kita akan melihat bagaimana memanfaatkan pustaka Apache Commons Net untuk berinteraksi dengan server FTP eksternal.

2. Penyiapan

Saat menggunakan pustaka, yang digunakan untuk berinteraksi dengan sistem eksternal, sering kali merupakan ide yang baik untuk menulis beberapa pengujian integrasi tambahan, untuk memastikan, kami menggunakan pustaka dengan benar.

Saat ini, kami biasanya menggunakan Docker untuk menjalankan sistem tersebut untuk pengujian integrasi kami. Namun, terutama ketika digunakan dalam mode pasif, server FTP bukanlah aplikasi termudah untuk dijalankan secara transparan di dalam wadah jika kita ingin menggunakan pemetaan port dinamis (yang seringkali diperlukan untuk pengujian agar dapat dijalankan pada server CI bersama. ).

Itu sebabnya kami akan menggunakan MockFtpServer sebagai gantinya, server FTP Palsu / Stub yang ditulis di Java, yang menyediakan API ekstensif agar mudah digunakan dalam pengujian JUnit:

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

Disarankan untuk selalu menggunakan versi terbaru. Itu bisa ditemukan di sini dan di sini.

3. Dukungan FTP di JDK

Anehnya, sudah ada dukungan dasar untuk FTP dalam beberapa varian JDK dalam bentuk sun.net.www.protocol.ftp.FtpURLConnection .

Namun, kita tidak boleh menggunakan kelas ini secara langsung dan itu mungkin untuk menggunakan java.net JDK . Kelas URL sebagai abstraksi.

Dukungan FTP ini sangat mendasar, tetapi dengan memanfaatkan kemudahan API java.nio.file.Files, itu mungkin cukup untuk kasus penggunaan sederhana:

@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 }

Karena dukungan FTP dasar ini sudah tidak memiliki fitur dasar seperti daftar file, kami akan menggunakan dukungan FTP di pustaka Apache Net Commons dalam contoh berikut.

4. Menghubungkan

Pertama-tama kita harus terhubung ke server FTP. Mari kita mulai dengan membuat kelas FtpClient.

Ini akan berfungsi sebagai API abstraksi ke klien FTP Apache Commons Net 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 membutuhkan alamat server dan port, serta nama pengguna dan kata sandi. Setelah menghubungkan itu perlu untuk benar-benar memeriksa kode balasan, untuk memastikan koneksi berhasil. Kami juga menambahkan PrintCommandListener , untuk mencetak tanggapan yang biasanya kami lihat saat menghubungkan ke server FTP menggunakan alat baris perintah ke stdout.

Karena tes integrasi kami akan memiliki beberapa kode boilerplate, seperti memulai / menghentikan MockFtpServer dan menghubungkan / memutuskan klien kami, kami dapat melakukan hal-hal ini dalam metode @Before dan @After :

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 mengatur port kontrol server tiruan ke nilai 0, kami memulai server tiruan dan port acak gratis.

Itulah mengapa kita harus mengambil port sebenarnya saat membuat FtpClient setelah server dijalankan, menggunakan fakeFtpServer.getServerControlPort () .

5. Daftar File

Kasus penggunaan aktual pertama adalah daftar file.

Mari kita mulai dengan tes dulu, gaya TDD:

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

Implementasinya sendiri sama mudahnya. Untuk membuat struktur data yang dikembalikan sedikit lebih sederhana demi contoh ini, kami mengubah array FTPFile yang dikembalikan diubah menjadi daftar 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. Mendownload

Untuk mengunduh file dari server FTP, kami sedang mendefinisikan API.

Di sini kami mendefinisikan file sumber dan tujuan pada sistem file lokal:

@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 berisi API yang nyaman, yang akan langsung menulis ke OutputStream yang ditentukan . Artinya kita bisa menggunakan ini secara langsung:

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

7. Mengupload

MockFtpServer menyediakan beberapa metode berguna untuk mengakses konten sistem berkasnya. Kita dapat menggunakan fitur ini untuk menulis tes integrasi sederhana untuk fungsionalitas pengunggahan:

@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(); }

Mengupload file bekerja dengan API yang mirip dengan mendownloadnya, tetapi alih-alih menggunakan OutputStream , kita perlu menyediakan InputStream sebagai gantinya:

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

8. Kesimpulan

Kita telah melihat, bahwa menggunakan Java bersama dengan Apache Net Commons memungkinkan kita untuk dengan mudah berinteraksi dengan server FTP eksternal, untuk akses baca dan tulis.

Seperti biasa, kode lengkap untuk artikel ini tersedia di repositori GitHub kami.