Рубрики
Без рубрики

Реализация FTP-клиента на Java

Узнайте, как легко взаимодействовать с внешним FTP-сервером на Java.

Автор оригинала: baeldung.

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

Удивительно, но в некоторых вариантах JDK уже есть базовая поддержка FTP в виде Удивительно, но в некоторых вариантах JDK уже есть базовая поддержка FTP в виде .

Однако мы не должны использовать этот класс напрямую, и вместо этого можно использовать JDK java.net. Класс URL как абстракция.

Эта поддержка FTP очень проста, но использует удобные API-интерфейсы java.nio.file.Файлы, этого может быть достаточно для простых случаев использования:

@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 абстракции для реального FTP-клиента Apache Commons Net:

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, мы запускаем фиктивный сервер и свободный случайный порт.

Вот почему мы должны получить фактический порт при создании Ftp-клиента после запуска сервера, используя fakeFtpServer.getServerControlPort() .

5. Список файлов

Первым фактическим вариантом использования будет список файлов.

Давайте сначала начнем с теста в стиле TDD:

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

Сама реализация столь же проста. Чтобы сделать возвращаемую структуру данных немного проще для этого примера, мы преобразуем возвращаемый FTPFile массив преобразуется в список Строк с помощью Java 8 Потоков:

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
}

FTP-клиент Apache Net Commons содержит удобный API, который будет напрямую записывать данные в определенный выходной поток. Это означает, что мы можем использовать это напрямую:

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 .