Внедряване на FTP-клиент в Java

1. Общ преглед

В този урок ще разгледаме как да използваме библиотеката на Apache Commons Net за взаимодействие с външен FTP сървър.

2. Настройка

Когато използвате библиотеки, които се използват за взаимодействие с външни системи, често е добра идея да напишете някои допълнителни тестове за интеграция, за да сте сигурни, че използваме библиотеката правилно.

Днес обикновено използваме Docker, за да завъртим тези системи за нашите тестове за интеграция. Въпреки това, особено когато се използва в пасивен режим, FTP сървърът не е най-лесното приложение, което да се изпълнява прозрачно в контейнер, ако искаме да използваме динамични картографирания на портове (което често е необходимо, за да могат тестовете да се изпълняват на споделен CI сървър ).

Ето защо вместо това ще използваме MockFtpServer, фалшив / зашеметяващ FTP сървър, написан на Java, който предоставя обширен API за лесно използване при тестове JUnit:

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

Препоръчва се винаги да използвате най-новата версия. Те могат да бъдат намерени тук и тук.

3. Поддръжка на FTP в JDK

Изненадващо, вече има основна поддръжка за FTP в някои вкусове на JDK под формата на sun.net.www.protocol.ftp.FtpURLConnection .

Не бива обаче да използваме този клас директно и вместо това е възможно да използваме java.net на JDK . Клас на URL като абстракция.

Тази FTP поддръжка е много основна, но използвайки удобните приложни програмни интерфейси (API) на java.nio.file.Files, може да е достатъчно за прости случаи на използване:

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

Тъй като при тази основна FTP поддръжка вече липсват основни функции като списъци с файлове, ще използваме FTP поддръжка в библиотеката Apache Net Commons в следващите примери.

4. Свързване

Първо трябва да се свържем с FTP сървъра. Нека започнем със създаването на клас FtpClient.

Той ще служи като API за абстракция на действителния Apache Commons Net FTP клиент:

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

Нуждаем се от адреса на сървъра и порта, както и потребителското име и паролата. След свързването е необходимо действително да проверите кода за отговор, за да сте сигурни, че свързването е било успешно. Също така добавяме PrintCommandListener , за да отпечатаме отговорите, които обикновено виждаме при свързване към FTP сървър, използвайки инструменти за команден ред за stdout.

Тъй като нашите интеграционни тестове ще имат някакъв шаблон код, като стартирате / спирате MockFtpServer и свързване / прекъсване на нашите клиенти, ние можем да направим тези неща в @Before и @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(); } }

Като задаваме контролния порт на фалшивия сървър на стойност 0, стартираме фалшивия сървър и безплатен произволен порт.

Ето защо трябва да извлечем действителния порт, когато създаваме FtpClient след стартиране на сървъра, използвайки fakeFtpServer.getServerControlPort () .

5. Изброяване на файлове

Първият действителен случай на използване ще бъде изброяване на файлове.

Нека започнем първо с теста, в TDD стил:

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

Самото изпълнение е еднакво просто. За да направим върнатата структура на данните малко по-опростена в името на този пример, ние трансформираме върнатия масив FTPFile се трансформира в списък от низове с помощта на 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. Изтегляне

За изтегляне на файл от FTP сървъра дефинираме API.

Тук дефинираме изходния файл и местоназначението в локалната файлова система:

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

Fpa клиентът на Apache Net Commons съдържа удобен API, който директно ще записва в определен OutputStream. Това означава, че можем да използваме това директно:

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

7. Качване

MockFtpServer предоставя някои полезни методи за достъп до съдържанието на своята файлова система. Можем да използваме тази функция, за да напишем прост тест за интеграция на функцията за качване:

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

Качването на файл работи по API доста подобно на изтеглянето му, но вместо да използваме OutputStream , вместо това трябва да предоставим InputStream :

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

8. Заключение

Видяхме, че използването на Java заедно с Apache Net Commons ни позволява лесно да взаимодействаме с външен FTP сървър за достъп за четене и запис.

Както обикновено, пълният код за тази статия е достъпен в нашето хранилище на GitHub.