ftplib presenta dos clases: FTP
y FTP_TLS
. Ambas implementan el lado del cliente del protocolo de transferencia de archivos, pero la segunda aporta algunas funciones extra para trabajar con conexiones seguras. Es también utilizado por urllib
para manejar URLs que usen FTP, indica la documentación.
En este artículo veremos cómo funciona y cómo utilizar el módulo. Luego, haremos una clase que presenta una pequeña API de mayor nivel para trabajar con el protocolo.
Para comenzar vamos a ver el ejemplo de una sesión básica con algunas tareas que presenta la documentación:
from ftplib import FTP ftp = FTP('ftp.debian.org') ftp.login() ftp.cwd('debian') ftp.retrlines('LIST') ftp.retrbinary('RETR README', open('README', 'wb').write) ftp.quit()
Veamos línea por línea:
from ftplib import FTP
Importa la clase FTP
, necesaria para establecer una conexión, enviar y recibir datos.
ftp = FTP('ftp.debian.org')
Se crea una instancia de dicha clase. Toma como argumentos host
, user
, passwd
, acct
y timeout
siendo todos opcionales. Por defecto el puerto 21 es utilizado. Si se quisiera especificar uno diferente, se debe hacer:
ftp = FTP() ftp.connect('ftp.debian.org', 22)
De esta manera se estaría utilizando el puerto 22.
ftp.login()
Ingresa con el usuario y contraseña especificados. Por defecto estos son «anonymous» y «anonymous@», respectivamente. Un tercer parámetro denominado acct
(que por defecto es una cadena vacía) indica el nombre de la cuenta. No todos los clientes soportan ACCT
, como así tantos otros siempre utilizan «ACCT noaccount».
ftp.login("admin", "admin123", "noaccount")
… podría haberse utilizado para especificar como usario «admin», «admin123» como contraseña y «noaccount» como datos de la cuenta.
ftp.cwd('debian')
La función FTP.cwd()
es utilizada para cambiar de directorio o carpeta (change working directory). En este caso, luego de acceder como un usuario anónimo cambia la ubicación a la carpeta debian
.
ftp.retrlines('LIST')
Retorna información sobre los archivos y carpetas en la ubicación actual. LIST
es un comando definido por el protocolo, así como otros que también pueden aplicarse en esta función como RETR
, NLST
o MLSD
. Para más información sobre los comandos véase RFC 959. El segundo parámetro es la función callback a la que se llama por cada dato recibido, por ejemplo:
def callback(info): print info ... ftp.retrlines('LIST', callback) ...
Resulta en lo mismo que en el código original, ya que el callback por defecto imprime la información en sys.stdout
.
ftp.retrbinary('RETR README', open('README', 'wb').write)
Ejecuta el comando RETR
para descargar el archivo README
en modo binario. El segundo parámetro es una función callback que será llamada por cada bloque de bytes recibidos, que a su vez estos son pasados como argumento a dicha función. En este caso se pasa la función write
de un objeto file
. Sin embargo, el archivo nunca se cierra, así que podría hacerse:
with open('README', 'wb') as f: ftp.retrbinary('RETR README', f.write)
El tercer parámetro opcional maxblocksize
indica el tamaño máximo que leerá el socket que utiliza a bajo nivel (por defecto 8192). Si se especifica el último parámetro rest
, un comando REST
es enviado al servidor pasándolo como argumento (None
por defecto).
ftp.quit()
Envía el comando QUIT
al servidor para cerrar la conexión. Nótese que si el servidor responde con una excepción, FTP.close()
será utilizado. Una vez aplicado cualquiera de los dos métodos no es posible reutilizar la instancia.
Obsérvese el siguiente código en donde se ejecutan varias funciones junto con su explicación. Nótese que para algunas funciones es necesario tener los privilegios correspondientes:
from ftplib import FTP, error_perm # ... ftp.dir() # Imprime el contenido de la carpeta actual print ftp.pwd() # Imprime la ruta actual ("/debian") ftp.mkd("nueva_carpeta") # Crea una nueva carpeta ((m)a(k)e (d)irectory) ftp.rmd("nueva_carpeta") # La elimina ((r)e(m)ove (d)irectory) # Retorna el tamaño del archivo especificado. El comando "SIZE" # no es estándar, por lo que retorna None en caso de fallar. # De lo contrario, retorna un entero representando los bytes. readme_size = ftp.size("README") if readme_size is None: print "Comando SIZE no soportado." else: print "README: %d bytes." % readme_size ftp.rename("README", "LEEME") # Renombrar un archivo try: # Envia un comando y retorna la respuesta print ftp.sendcmd("SIZE LEEME") except error_perm as e: print e ftp.delete("LEEME") # Eliminar
Conexión segura
Para esto el módulo brinda la clase FTP_TLS
, que hereda de FTP
definiendo cuatro objetos adicionales: ssl_version
, auth()
, prot_p()
y prot_c()
. Obsérvese el siguiente ejemplo brindado por la documentación:
from ftplib import FTP_TLS ftps = FTP_TLS('ftp.python.org') ftps.login() # ingresar como anónimo ftps.prot_p() # cambiar a conexión segura ftps.retrlines('LIST') ftps.quit()
Puedes encontrar la descripción detallada en este enlace.
Cargar y descargar archivos
Por último una pequeña API para subir y descargar archivos vía FTP, sin necesidad de tener conocimiento de los comandos propios del protocolo.
xftp.py (descargar como ZIP)
#!/usr/bin/env python # -*- coding: utf-8 -*- from ftplib import FTP class XFTP(FTP): """API de alto nivel para cargar y descargar archivos vía FTP""" def upload(self, filename, callback=None): with open(filename, "rb") as f: self.storbinary("STOR " + filename, f, callback=callback) def download(self, filename, callback=None): cmd = "RETR " + filename if callback is None: # Usar la callback por defecto with open(filename, "wb") as f: self.retrbinary(cmd, f.write) else: # Callback del usuario self.retrbinary(cmd, callback)
Ejemplo:
from xftp import XFTP from ftplib import error_perm from os import rename ftp = XFTP("ftp.debian.org") ftp.login() ftp.cwd("debian") ftp.download("README") rename("README", "LEER") try: ftp.upload("LEER") except error_perm: print "No tienes los permisos suficientes." ftp.quit()
Especificando callbacks:
with open("archivo_recibido", "wb") as f: ftp.download("README", f.write)
Versión
Python 2.7
Curso online 👨💻
¡Ya lanzamos el curso oficial de Recursos Python en Udemy!
Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter
en 2024.
Consultoría 💡
Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.
Raycris Maldonado says:
Como yo puedo recorrer multiples directorios recurisvamente con esa libreria? alguien sabe
Raycris Maldonado says:
Esto fue lo que me sirvio para recorrer multiples carpetas:
auxAlmacenChrysler = CHRYSLER_PIEZAS + 'Auxiliar de almacen'
ftp.cwd(auxAlmacenChrysler)
print('\nNos encontramos en la carpeta: '+ ftp.pwd() + "\n")
ftp.dir();
CHRYSLER_PIEZAS -> Es una constante la cual tiene el path donde quiero entrar.
Auxiliar de almacen – > Es el nombre de la carpeta final la cual debo entrar.
ftp.cwd(auxAlmacenChrysler)
-> Entra a la carpeta con el path indicado.Recursos Python says:
Gracias por compartirlo, saludos.