Всем привет! Это будет очень маленькая статья. Наша задача тут: подключиться к локальному серверу FTP (я выбрала FileZilla) и отправить туда чего-нибудь используя (очевидно) FTP протокол.

Итак, капелька теории


FTP (File Transfer Protocol) — протокол передачи файлов по сети.
Для работы по FTP нужны двое: FTP-сервер и FTP-клиент.

Сервер обеспечивает доступ по логину и паролю к нужным файлам и, соответственно, показывает пользователю только те файлы и папки, к которым он имеет доступ.
Клиент — программа для FTP-соединения. Основная идея в том, чтобы пользователь мог оперировать файловыми данными на стороне сервера: просматривать, редактировать, копировать, загружать и удалять.

Все логично.

В отличие, скажем, от того же HTTP FTP использует в качестве модели соединения двойное подключение. При этом один канал является управляющим, через который поступают команды серверу и возвращаются его ответы (обычно через TCP-порт 21), а через остальные происходит собственно передача данных, по одному каналу на каждую передачу. Поэтому в рамках одной сессии по протоколу FTP можно передавать одновременно несколько файлов, причём в обоих направлениях.

Если расписать процесс установки соединения и дальнейшей передачи данных по пунктам, то получится примерно следующее:

  1. Пользователь активирует клиентское приложение и соединяется с сервером, введя логин и пароль.
  2. Устанавливается управляющее соединение между соответствующими модулями — интерпретаторами протокола со стороны клиента и сервера.
  3. Пользователь посредством клиента посылает команды серверу, определяющие различные параметры FTP-соединения (активный или пассивный режим, FTP — порт, вид передачи данных, их тип), а также директивы для действий, которые юзер намерен осуществить (например, удалить, переименовать, закачать файл и т.д.).
  4. Далее один из участников (к примеру, клиент), являющийся пассивным, становится в режим ожидания открытия соединения на FPT — порт, который задан для передачи информации.
    Затем активный участник открывает соединение и начинает передавать данные по предназначенному для этого каналу.
  5. По завершении передачи, это соединение закрывается, но управляющий канал между интерпретаторами остается открытым, вследствие чего пользователь в рамках той же сессии может вновь открыть передачу данных.

FTP — соединение по умолчанию происходит через порт 21, если не установлен другой порт.

image

Реализация


В данном случае будем использовать класс FTPClient из библиотеки Apache (org.apache.commons.net.ftp.FTPClient). Создаем объект класса, устанавливаем таймаут. Затем мы должны сначала подключиться к серверу с помощью connect, прежде чем что-либо делать (и не забыть отключиться после того, как все сделаем). В connect в качестве первого параметра пишем локальный адрес хоста, вторым аргументом — порт для подключения. Также необходимо проверить код ответа FTP (слишком очевидно, но все же), чтобы убедиться, что соединение было успешным.

import org.apache.commons.net.ftp.FTP
import org.apache.commons.net.ftp.FTPClient
import org.apache.commons.net.ftp.FTPReply

...

val con = FTPClient()
        con.connectTimeout = ххх
        con.connect("192.168.0.105", 21)
        if (FTPReply.isPositiveCompletion(con.replyCode)) {
                //все круто
        }

Хорошо, мы подключились к серверу. Что дальше? Теперь необходимо пройти авторизацию под именем пользователя, которого мы, собственно, создали для этого. Но перед этим нужно установить для текущего режима подключения к данным значение PASSIVE_LOCAL_DATA_CONNECTION_MODE с помощью команды enterLocalPassiveMode(). Это значит, что все передачи будут происходить между клиентом и сервером, и что сервер находится в пассивном режиме, ожидая подключения клиента для инициализации передачи данных. (например, ACTIVE_LOCAL_DATA_CONNECTION_MODE подразумевает, что инициатором подключения будет выступать сервер).

После в login пишем логин и пароль пользователя. storeFile() сохраняет файл на сервере, используя заданное имя и принимая входные данные из заданного InputStream. У меня была задача периодически писать в определенный файл на сервере, поэтому это выглядит так:

con.enterLocalPassiveMode()
                if (con.login("nad", "nad")) { //вводим логин и пароль (да, вот так в открытом виде)
                    con.setFileType(FTP.BINARY_FILE_TYPE)
                    val msg = "your_msg"
                    val msg_bytes: InputStream = ByteArrayInputStream(msg.toByteArray())
                    val result: Boolean = con.storeFile("/sms.txt", msg_bytes)
                    msg_bytes.close()
                    if (result) Log.v("upload result", "succeeded")
                    con.logout()
                    con.disconnect()
                }
            }
             catch (ex: IOException) {
                ex.printStackTrace()
                return Result.failure()
            }

Если же вы хотите скопировать свой файл на сервер целиком, можно сделать так (суть не особо меняется, но вдруг кому-то нужно):

val file = File("путь до файла")
val msg_bytes = FileInputStream(file)

В целом это все, что вам может понадобиться. И, конечно, немного о настройке локального FTP сервера. У меня это, как и писала ранее, FileZilla. Все оставляем по дефолту. Создаем пользователя и присваиваем ему папку на хосте, из которой он может читать/писать/удалять и т.д.

image

Вот и все. Запускаем и смотрим логи на сервере. И вот чего должны получить:

image

Удачи!