Blog
Definir el Protocolo de Transferencia de Archivos
Utilizar librerías predeterminadas y de terceros en Python para crear clientes y servidores FTP
El Protocolo de Transferencia de Archivos (FTP por sus siglas en inglés) es un protocolo de red que sirve para la transferencia de archivos entre sistemas conectados a una red por el protocolo TCP y está basado en la arquitectura cliente-servidor. Este protocolo es independiente del sistema operativo entre los sistemas, es decir, las transferencias pueden realizarse sin importar si el cliente y servidor tienen el mismo sistema operativo. En este artículo explicaremos como usar la librería ftplib
de Python para crear un cliente FTP en Python.
Cliente FTP en Python
Un cliente FTP emplea este protoloco para conectarse a un servidor FTP y transferir archivos a un alojamiento. La librería ftplib
nos provee muchas funciones necesarias para conectarnos a un cliente FTP, descargar y subir archivos. Veamos un ejemplo en la consola interactiva de Python, nos conectaremos a un servidor FTP de pruebas (pueden encontrarlo aqui) e ingresaremos las credenciales.
In [1]: import ftplib In [2]: ftp = ftplib.FTP('ftp.dlptest.com') In [3]: ftp.login('dlpuser@dlptest.com', 'e73jzTRTNqCN9PYAAjjn') Out[3]: '230 OK. Current restricted directory is /' In [4]: ftp.retrlines('LIST') drwxr-xr-x 2 colora17 colora17 49152 Oct 25 12:50 . drwxr-xr-x 2 colora17 colora17 49152 Oct 25 12:50 .. -rw-r--r-- 1 colora17 colora17 753664 Oct 25 12:49 1_2159874062056749010_17-9ULspeedtest.upt -rw-r--r-- 1 colora17 colora17 4934965 Oct 25 12:49 2018.08.11_ngy-vs-kas_ngy-gabriel-augusto-xavier_foul_0:24_241.mp4 -rw-r--r-- 1 colora17 colora17 8 Oct 25 12:55 FTP.txt -rw-r--r-- 1 colora17 colora17 22 Oct 25 12:50 syslogtest_bw.txt Out[4]: '226-Options: -a -l \n226 6 matches total' In [5]: ftp.retrbinary('RETR syslogtest_bw.txt', open('syslogtest_bw.txt', 'wb').write) Out[5]: '226-File successfully transferred\n226 0.000 seconds (measured here), 303.41 Kbytes per second' In [6]: ftp.quit() Out[6]: '221-Goodbye. You uploaded 0 and downloaded 1 kbytes.\n221 Logout.'
OK, tenemos varias funciones que no conocemos así que vamos a recorrer este ejemplo, línea por línea para ver que hace cada una de estos métodos:
- [1] Importamos la librería
ftplib
. Aquí se encuentran las funciones que necesitamos para trabajar con servidores FTP. - [2] Creamos una variable
ftp = ftplib.FTP('ftp.dlptest.com')
. El constructorFTP(host)
retorna un objeto de la clase FTP. Cuando le pasamos un host (una URL FTP) esta clase llama a la funciónconnect(host)
. Esta clase soporta su uso por medio de la sentenciawith
. - [3] Con nuestro objeto FTP creado, llamamos a la función
FTP.login(user, passwd)
. Pasamos las credenciales del servidor para conectarnos. Si no le damos argumentos a esta función, su valor predeterminado será user=”anonymous”. - [4] Llamamos a la función
FTP.retrlines(cmd)
. Esta función retorna una lista de archivos o directorios en el servidor FTP. El parámetro cmd, debe ser un argumento de RETR apropiado (para más información sobre estos argumentos, aquí). - [5] Hacemos una llamada a la función
FTP.retrbinary(cmd, callback)
. Esta función entrega un archivo en modo de transferencia. El parámetro cmd debe ser un comando de RETR apropiado (“RETR filename“). El parámetro callback es una función que se ejecutará por cada bloque de datos que recibidos, en este caso creamos un archivo en modo escritura binaria (wb) y escribimos los datos. - [6] Cerramos la conexión FTP
Navegando a través el servidor
Movernos a traves de los directorios con nuestro clientre FTP en Python depende de dos funciones fundamentalmente: FTP.cwd(pathname)
y FTP.retrlines(cmd, callback=None)
. Ya hemos visto el funcionamiento de la segunda función, veamos como usar la función cwd:
In [1]: import ftplib In [2]: ftp = ftplib.FTP('ftp.dlptest.com') In [3]: ftp.login('dlpuser@dlptest.com', 'e73jzTRTNqCN9PYAAjjn') Out[3]: '230 OK. Current restricted directory is /' In [4]: ftp.mkd('odar') Out[4]: 'odar' In [5]: ftp.retrlines('LIST') drwxr-xr-x 3 colora17 colora17 49152 Oct 25 13:26 . drwxr-xr-x 3 colora17 colora17 49152 Oct 25 13:26 .. -rw-r--r-- 1 colora17 colora17 772096 Oct 25 13:19 1_2159874062056749010_17-9ULspeedtest.upt -rw-r--r-- 1 colora17 colora17 8 Oct 25 13:26 FTP.txt -rw-r--r-- 1 colora17 colora17 4 Oct 25 13:18 UM_test_after.txt drwxr-xr-x 2 colora17 colora17 4096 Oct 25 13:26 odar -rw-r--r-- 1 colora17 colora17 0 Oct 25 13:10 test.bin Out[5]: '226-Options: -a -l \n226 7 matches total' In [6]: ftp.cwd('odar') Out[6]: '250 OK. Current directory is /odar'
La función FTP.cwd(pathname)
nos ubica directamente en la ruta que le pasemos como argumento.
Descargando archivos con cliente FTP en Python
La librería ftplib
nos da la capacidad de descargar archivos o directorios del servidor FTP al que estemos conectados. La función FTP.retrbinary(cmd, fp)
nos permite descargar abrir un archivo del servidor en modo binario y escribir su contenido en un archivo local.
In [1]: import ftplib In [2]: ftp = ftplib.FTP('ftp.dlptest.com') In [3]: ftp.login('dlpuser@dlptest.com', 'e73jzTRTNqCN9PYAAjjn') Out[3]: '230 OK. Current restricted directory is /' In [4]: ftp.retrlines('LIST') drwxr-xr-x 2 colora17 colora17 49152 Oct 26 16:10 . drwxr-xr-x 2 colora17 colora17 49152 Oct 26 16:10 .. -rw-r--r-- 1 colora17 colora17 731136 Oct 26 16:13 1_2159874062056749010_17-9ULspeedtest.upt -rw-r--r-- 1 colora17 colora17 8 Oct 26 16:15 FTP.txt Out[4]: '226-Options: -a -l \n226 4 matches total' In [5]: ftp.retrbinary('RETR FTP.txt', open('archivodescargado.txt', 'wb').wr ...: ite) Out[5]: '226-File successfully transferred\n226 0.000 seconds (measured here), 90.77 Kbytes per second' In [6]: import os In [7]: os.listdir() Out[7]: ['archivo1.txt', 'archivodescargado.txt', 'file1.txt', 'archivo2.txt', 'archivo3.txt']
Tal vez cuando ejecutes este código tengas resultados diferentes (este servidor de prueba borra todos los archivos creados en 30 minutos).
Como podemos ver, la función FTP.retrbinary(cmd, fp)
nos permite descargar un archivo del servidor al que estemos conectados. El parámerto cmd es un comando de RERT apropiado y el parámetro fp es el archivo en el que guardaremos los datos descargados.
Subiendo archivos con cliente FTP en Python
Una conexión FTP va en ambas direcciones, no solo podemos descargar archivos desde el servidor si no también subir archivos desde nuestro cliente (PC). Para esto, la librería ftplib
nos proporciona dos funciones: FTP.storlines(cmd, fp)
y FTP.storbinary(cmd, fp)
.
FTP.storelines(cmd, fp) nos permite subir archivos en modo ASCII (texto) y FTP.strobinary(cmd, fp), nos sirve para subir archivos en modo binario (imágenes, audio). Su uso es bastante parecido a la función FTP.retrbinary(cmd, fp)
, solo que esta vez, tenemos que abrir nuestro archivo en modo lectura binaria y subirlo al servidor.
In [1]: import ftplib, os In [2]: ftp = ftplib.FTP('ftp.dlptest.com','dlpuser@dlptest.com', 'e73jzTRTNq ...: CN9PYAAjjn') In [3]: file = open('Screenshot_20181026_193145.png', 'rb') In [4]: ftp.storbinary('STOR Screenshot_20181026_193145.png', file) Out[4]: '226-File successfully transferred\n226 5.134 seconds (measured here), 48.09 Kbytes per second' In [5]: ftp.retrlines('LIST') drwxr-xr-x 2 colora17 colora17 49152 Oct 26 16:36 . drwxr-xr-x 2 colora17 colora17 49152 Oct 26 16:36 .. -rw-r--r-- 1 colora17 colora17 772096 Oct 26 16:33 1_2159874062056749010_17-9ULspeedtest.upt -rw-r--r-- 1 colora17 colora17 17202685 Oct 26 16:34 1_627440375109838991_17-9ULspeedtest.upt -rw-r--r-- 1 colora17 colora17 8 Oct 26 16:39 FTP.txt -rw-r--r-- 1 colora17 colora17 252848 Oct 26 16:39 Screenshot_20181026_193145.png Out[5]: '226-Options: -a -l \n226 6 matches total' In [6]: text_file = open('archivo2.txt', 'rb') In [7]: text_file Out[7]: <_io.BufferedReader name='archivo2.txt'> In [8]: ftp.storbinary('STOR archivo2.txt', text_file) Out[8]: '226-File successfully transferred\n226 0.160 seconds (measured here), 6.74 Kbytes per second' In [9]: ftp.retrlines('LIST') drwxr-xr-x 2 colora17 colora17 49152 Oct 26 16:42 . drwxr-xr-x 2 colora17 colora17 49152 Oct 26 16:42 .. -rw-r--r-- 1 colora17 colora17 772096 Oct 26 16:33 1_2159874062056749010_17-9ULspeedtest.upt -rw-r--r-- 1 colora17 colora17 17202685 Oct 26 16:34 1_627440375109838991_17-9ULspeedtest.upt -rw-r--r-- 1 colora17 colora17 8 Oct 26 16:48 FTP.txt -rw-r--r-- 1 colora17 colora17 252848 Oct 26 16:39 Screenshot_20181026_193145.png -rw-r--r-- 1 colora17 colora17 1104 Oct 26 16:42 archivo2.txt Out[9]: '226-Options: -a -l \n226 7 matches total' In [10]:
En nuestro caso, decidimos subir una imagen con la función FTP.storbinary(cmd, fp), creamos un objeto file en Python, vinculado a nuestra imagen en modo binario, y hacemos la llamada a la función. Luego hacemos una subida de un archivo de texto.
Creando un servidor FTP con Python
Ya vimos como crear un cliente FTP en Python pero, ¿qué si queremos crear un programa que use un servidor FTP?. Python también nos permite hacer esto con la librería de terceros pyftpdlib
. Esta librería nos permite crear un servidor FTP, autentificar usuarios, limitar conexiones entrantes y puertos pasivos, entre otras características.
import os import logging from pyftpdlib.authorizers import DummyAuthorizer from pyftpdlib.handlers import FTPHandler from pyftpdlib.servers import FTPServer def main(): # Instancia un autorizador dummy para controlar usuarios "virtuales" authorizer = DummyAuthorizer() # Define un nuevo usuario teniendo todos los permisos y otro para # usuarios de solo lectura authorizer.add_user('user', '12345', '.', perm='elradfmwMT') authorizer.add_anonymous(os.getcwd()) # Instancia una clase controladora de FTP handler = FTPHandler handler.authorizer = authorizer # Define una string predeterminada para cuando alguien se conecte # al cliente handler.banner = 'pyftpdlib basado en FTP, listo' logging.basicConfig(filename='pyftpd.log', level=logging.INFO) # Instancia una clase servidor FTP y abre conexión en # 0.0.0.0:2121 address = ('127.0.0.1', 2121) server = FTPServer(address, handler) # configura un limite de conexiones server.max_cons = 256 server.max_cons_per_ip = 5 # Inicia el servidor FTP server.serve_forever() if __name__ == '__main__': main()
Con esto tendremos un servidor FTP básico ejecutándose en el background. Podemos entrar como usuarios anónimos o como usuarios autorizados con ID y contraseña. Para iniciarlo tenemos que ejecutar python ftpserver.py &
, el & ejecuta el comando y no entra en su proceso sino que lo deja en el background y nos regresa a la línea de consola. Ahora intentemos conectarnos desde la consola interactiva de Python:
In [1]: import ftplib In [2]: ftp = ftplib.FTP('') In [3]: ftp.connect('127.0.0.1', 2121) Out[3]: '220 pyftpdlib basado en FTP, listo' In [4]: ftp.login('user', '12345') Out[4]: '230 Login successful.' In [5]: ftp.retrlines('LIST') drwxr-xr-x 2 odars users 4096 Oct 12 18:52 .vscode drwxr-xr-x 2 odars users 4096 Oct 22 21:24 __pycache__ drwxr-xr-x 2 odars users 4096 Oct 26 23:32 archivosprueba -rw-r--r-- 1 odars users 1160 Oct 27 00:36 ftpserver.py Out [5]: '226 Transfer complete.'
➡ Continúa aprendiendo sobre redes en nuestro Curso de redes con Python: