Version en ligne

Tutoriel : Utiliser le module FTP de Python

Table des matières

Utiliser le module FTP de Python
Avant-propos
Toutes les fonctions, une à une
Mettons en oeuvre nos connaissances

Utiliser le module FTP de Python

Avant-propos

Beaucoup d'entre vous sont certainement en période d'apprentissage de Python (comme moi), et à cela rien de plus normal, car Python nous offre énormément d'outils très performants pour développer !

Dans ce tutoriel, je vais vous parler d'un module en particulier, ftplib.

Si vous utilisez un client FTP (ce dernier étant un protocole, à l'instar du HTTP par exemple) tel que Filezilla, j'ai le plaisir de vous dire que vous pourrez faire la même chose (enfin presque :-° ) après la lecture de mon tutoriel !
À ceci près que vous le ferez en console et non en fenêtre. :p Par la suite, ce serait un bon exercice d'adapter le script à un « mode » fenêtre avec PyQt, PyGTK, etc.

Avant-propos

Toutes les fonctions, une à une

Avant toute programmation, je tiens à préciser plusieurs petites choses.

Tout d'abord, sachez que je ne suis pas un « pro » de Python, il y a donc certainement une meilleure façon de coder que celle donnée ici.
Dans ce tutoriel, vous apprenez tout d'abord à vous servir du module, et non à coder.

D'ailleurs, je dois vous signaler que si vous n'avez pas lu le tutoriel rédigé par Gérard Swinnen (ou un équivalent), vous ne pourrez pas suivre le reste.
Je pense que les dix premiers chapitres suffiront, le reste n'étant pas utilisé dans mon cours.

Bon, maintenant, je vais essayer de vous donner envie de lire la suite, en vous donnant la liste des choses que vous saurez faire à la fin de votre lecture :

Comme vous pouvez le constater, la liste est longue !

À la fin du tutoriel, vous aurez un script qui permettra d'exécuter toutes ces commandes, mais il ne vous sera donné que vers la fin.
Quand j'expliquerai comment faire telle ou telle action, je ne vous donnerai que le strict minimum en code.


Toutes les fonctions, une à une

Toutes les fonctions, une à une

Avant-propos Mettons en oeuvre nos connaissances

Dans cette partie, je vous expliquerai en détail comment on utilise la fonction N ou Y.

J'ai décidé d'effectuer cela par "mini-chapitre", c'est-à-dire que chaque fonction aura son titre. :)

Grâce à cette mise en page, ce tuto vous sera utile comme référence lorsque vous coderez. ;)

La connexion

Cette étape est la plus importante, car toutes les autres découlent de la connexion !

Rassurez-vous, son utilisation est on ne peut plus simple. :)

host = "votre_host.com" # adresse du serveur FTP
user = "votre_pseudo" # votre identifiant
password = "votre password" # votre mot de passe
connect = ftp.ftplib(host,user,password) # on se connecte

Je ne vais pas détailler ce code, tout est dans les commentaires. :)

Déconnexion

Étape tout aussi importante, la déconnexion. À vrai dire, théoriquement, si vous fermez la console, la connexion se ferme. Mais dans de gros scripts, il vaut mieux la fermer.
Un inconvénient tout de même : une fois la connexion fermée, on ne peut plus la rouvrir !

connect.quit() # où "connect" est le nom de la variable dans laquelle vous avez déclaré la connexion !

Envoyer un fichier

Cette commande est certainement l'une des plus utilisées !
Encore une fois, elle est très simple.

Voici les lignes à insérer pour envoyer un fichier :

fichier = "C:\Python_exercices\ftp.py"
file = open(fichier, 'rb') # ici, j'ouvre le fichier ftp.py 
connect.storbinary('STOR '+fichier, file) # ici (où connect est encore la variable de la connexion), j'indique le fichier à envoyer
file.close() # on ferme le fichier

Comme vous le voyez, à la 3e ligne du code, on met 'STOR'+fichier.

Pourquoi on met ça ? o_O

Bonne question. Sachez que 'STOR' est une commande FTP (et non Python). Ici, on indique au serveur quelle action on va effectuer, c'est très important. J'ai effectué juste derrière une concaténation ; à la fin, cela donnera ceci :
STOR C:\Python_exercices\ftp.py.

État de la connexion

Lorsque l'on se connecte au serveur, il nous renvoie un message. Avec la fonction que je vais donner ci-dessous, nous allons récupérer ce message pour éventuellement l'afficher après. ;)

etat = connect.getwelcome() # grâce à la fonction getwelcome(), on récupère le "message de bienvenue"
print "Etat : ", etat # ici, on l'affiche, cette ligne est facultative mais si vous ne l'affichez pas, c'est bête :p

Lister les fichiers & dossiers du répertoire

Après plusieurs actions effectuées sur le serveur, vous avez envie de voir si tout s'est bien déroulé ?
Pour cela, il vous suffira de lister le répertoire, avec la fonction dir().

rep = connect.dir() # on récupère le listing
print rep # on l'affiche dans la console

Personnellement, je vous conseille d'afficher le listing du répertoire après chaque action de renommage, d'effaçage, etc. Cela vous permettra de voir si tout s'est bien passé. ;)

Renommer un fichier ou un dossier

Parmi les actions que j'ai citées dans la première partie, il y avait "Renommer un fichier ou un dossier". Ici, je vous explique comment le faire. :)

renommer = raw_input('Tapez le nom du fichier / dossier à renommer : ') # ici, vous devez entrer le nom du fichier à renommer, par exemple fichier.txt ou le nom du dossier sans les /
renommer_en = raw_input('Le renommer en : ') # le nom de sortie, vous pouvez aussi changer l'extension
rename = connect.rename(renommer, renommer_en) # maintenant, on effectue vraiment l'action

J'ai rajouté deux raw_input() afin de faciliter votre travail dans la console, mais la fonction pour renommer tient sur une seule ligne.

Effacer un fichier

Après "renommer un fichier", il y a évidemment "effacer un fichier". :p
Ici, c'est encore plus simple (si c'est possible o_O ), il faut juste indiquer le nom !

effacer = raw_input('Tapez le nom du fichier à effacer : ') # vous indiquez dans la console le fichier, ex. : fichier.py
delete = connect.delete(effacer) # là, c'est la fonction en elle-même

Créer un répertoire

Cela ne sert à rien d'expliquer en trente lignes, je vous laisse lire le code. :)

rep = raw_input('Tapez le nom du répertoire à créer : ') # entrez le nom du répertoire, sans les slash ( / )
repertoire = connect.mkd(rep) # la fonction pour le créer, "mkd()"

Supprimer un répertoire

Pour supprimer un dossier, ce n'est pas la même fonction que pour supprimer un fichier. Ça peut paraître logique, mais je tiens à le signaler.
Voici le code :

supprimer = raw_input('Tapez le nom du répertoire à supprimer : ') # entrez le nom du dossier
delete_dir = connect.rmd(supprimer) # la fonction rmd supprime le dossier

Envoyer une fonction FTP au serveur

Quand vous ouvrez votre client FTP (par exemple Filezilla), il a une partie qui concerne les commandes envoyées au serveur (ce n'est peut-être pas présent sur tous les clients FTP). Lorsque vous changez de répertoire, vous pouvez voir des lignes défiler, avec des noms bizarres...

Et c'est quoi, ces noms ?

Je ne vous le dirai pas, nananèreuh. :-°
Ce sont des commandes FTP ! Juste que là, Python nous servait d'intermédiaire entre le serveur et nous. Mais qu'envoie Python au serveur ? Pas des fonctions Python, quand même ? o_O
Eh bien non, tout simplement des commandes, c'est-à-dire des noms barbares du type "CWD".

Ici, je vais vous décrire la marche à suivre pour en envoyer, mais point vous lister toutes les commandes existantes !

commande = raw_input('Tapez la commande à effectuer : ') # entrez la commande à effectuer
resultat = connect.sendcmd(commande) # on envoie la commande au serveur : si elle n'est pas bonne, Python plantera

Je vous donne juste un exemple de commande FTP, qui vous servira forcément : CWD rep. Elle sert à changer le répertoire. Par exemple, pour aller au répertoire "Admin" de votre site :

connect.sendcmd('CWD Admin')

Recevoir la réponse du serveur suite à une action

Rassurez-vous, rien de plus simple à faire. :)
Imaginons que vous avez cette ligne en python :

rename = connect.rename('Admin', 'Administration') # action de renommage

Il vous suffira d'afficher la variable rename :

rename = connect.rename('Admin', 'Administration') # action de renommage
print rename # on affiche la réponse du serveur

Arrêter le transfert d'un fichier

Il existe une fonction (incluse au module ftplib) qui permet de stopper (ou « d'avorter ») le transfert d'un fichier.
Je vous donne la fonction, mais je vous indique que dans la partie suivante, dans le script que nous allons réaliser, elle n'y sera pas. En effet, lors du traitement d'une commande (donc ici, l'envoi d'un fichier), le script ne nous permet pas de renvoyer une autre commande ; du coup, mieux vaut fermer la fenêtre de la console, cela aura le même effet. :D

connect.abort() # abort() avorte le transfert

C'est la fin de cette partie !
Dans la dernière partie (c'est-à-dire la suivante), je vais vous montrer comment faire pour mettre nos connaissances à profit en créant un script Python contenant toutes les fonctions vues ensemble. :)


Avant-propos Mettons en oeuvre nos connaissances

Mettons en oeuvre nos connaissances

Toutes les fonctions, une à une

Dans cette dernière partie, je vous propose de faire un petit programme et toutes les fonctions que l'on a vues y seront.

Dans ce script, nous allons utiliser une notion importante, les décorateurs.

En gros, grâce à eux, vous pourrez appeler une fonction à l'initialisation d'une autre.
C'est un peu compliqué à expliquer ici : je préfère vous donner un lien vers un tutoriel traitant de ce sujet : Décorateurs.

Bon : maintenant, passons au vif du sujet.
Nous allons créer une fonction pour chaque action possible, et auxiliairement une aide pour chaque fonction. ;)

À l'entrée des commandes dans la console, on effectuera deux tests principaux :

Pour cela, on utilisera évidemment (c'est tellement plus simple :p ) try, except et compagnie.

Voici la bête :

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# ftp.py
# FTP CLI interface
#
# Copyright (C) 2007 Pierre "delroth" Bourdon <[email protected]>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
import ftplib as ftp # on importe le module et on la renomme juste pour le script en "ftp"
 
commands = {} # on crée le dictionnaire qui contiendra toutes les commandes &amp; descriptions
 
def connection(value=None):
    if value == None:
        return getattr(connection, 'value', None)
    else:
        connection.value = value
        return value
 
def ftpcommand(nom_court, description): # fonction pour ajouter la fonction au dictionnaire commands
    def decorator(function):
        global commands # on n'oublie pas de mettre le dico commands global, sinon ça va en redéfinir un
        commands[nom_court] = (function, description) # on ajoute au dictionnaire
        return function
    return decorator
 
@ftpcommand("help", "Affiche l'aide des commandes") # ici on appelle le décorateur : il va s'occuper d'ajouter la fonction help avec sa description au dictionnaire commands
def help(): # définition de la commande, juste après @ftpcommand car ON EST OBLIGÉS
    global commands
    keys = commands.keys() # on récupère les clés == fonctions dans notre cas
    keys.sort() # on trie par ordre alphabétique :)
    for i in keys:
        print i+" : "+commands[i][1] # on affiche le nom de la fonction et sa description
 
@ftpcommand("connect", "Se connecte au serveur. Syntaxe: connect <host> <user> <password>")
def connect(host, user, password):
    connection(ftp.FTP(host, user, password))
 
@ftpcommand("ls", "Liste le contenu du répertoire actuel")
def ls():
    print connection().dir() # on affiche le listing du répertoire
 
@ftpcommand("deco", "Se déconnecte du serveur. Syntaxe: deco")
def deco():
        connection().quit() # la déconnexion avec quit()
 
@ftpcommand("envoi", "Envoie un fichier au serveur. Syntaxe: envoi <adresse_fichier>")
def envoi(adresse_fichier):
      fichier = adresse_fichier
      file = open(fichier, 'rb') # on ouvre le fichier en mode "read-binary"
      connection().storbinary('STOR '+fichier, file) # envoi
      file.close() # fermeture du fichier
 
@ftpcommand("rename", "Renomme un fichier. Syntaxe: rename <avant> <apres>")
def rename(avant, apres):
        renommer, renommer_en = avant, apres
        rename = connection().rename(renommer, renommer_en) # on renomme
 
@ftpcommand("efface", "Efface un fichier. Syntaxe : efface <fichier>")
def efface(fichier):
        effacer = fichier
        delete = connection().delete(effacer) # on efface
 
@ftpcommand("creer_rep", "Crée un répertoire (dossier). Syntaxe : creer_rep <nom>")
def creer_rep(nom):
        rep = nom
        repertoire = connection().mkd(rep) # on crée le répertoire
 
@ftpcommand("sup_rep", "Supprimer un répertoire (dossier). Syntaxe : sup_rep <nom>")
def sup_rep(nom):
        supprimer = nom
        delete_dir = connection().rmd(supprimer) # on supprime le répertoire
 
@ftpcommand("cmd", "Envoie une commande au serveur. Syntaxe: cmd <commande>")
def cmd(commande):
        resultat = connection().sendcmd(commande) # on envoi la commande
 
# un petit message à propos de la license du script :)
welcome = '''ftp.py version 1.0, Copyright (C) 2007
 
ftp.py comes with ABSOLUTELY NO WARRANTY. This is free software, and you are
welcome to redistribute it under certain conditions; see the GNU General Public
License for more details: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html'''
 
def main():
    global commands
    print welcome # affichage du message ci-dessus
    def command_to_argv(cmd):
        argv = cmd.split(' ') # on met dans une liste les différents arguments
        argv_size = len(argv) # on compte le nombre de paramètres
        i = 0 # on initialise le compteur
        while i < argv_size:
            if argv[i].endswith('\\') and i + 1 != argv_size: # si c'est un nom de fichier du type : test\ 1.py
                argv[i] = argv[i][:-1] + " " + argv[i + 1] # on ajoute le "1.py" à "test", ce qui fait "test 1.py"
                del argv[i + 1]
                argv_size -= 1
            i += 1 # on incrémente le compteur
        return argv # on retourne les arguments
    while True: # boucle infinie, il va encore falloir utiliser break pour en sortir
        try: cmd = command_to_argv(raw_input('> ')) # si la commande entrée provoque une erreur...
        except EOFError: return 0 # on quitte la boucle
        except KeyboardInterrupt: return 0 # on quitte la boucle
 
        cmdname, args = cmd[0], cmd[1:] # cmdname = fonction appelée, args = arguments
        if not cmdname in commands.keys(): # si la fonction appelée n'existe pas dans le script
            print "Erreur: '%s' commande incorrecte." % cmdname # on affiche un message d'erreur
            continue
        try:
            commands[cmdname][0](*args)
        except TypeError:
            print "Erreur: mauvais nombre d'arguments pour '%s' command." % cmdname
        except AttributeError:
            print "Erreur : vous n'êtes pas connecté !"
    return 0
 
import sys
if __name__ == "__main__": sys.exit(main()) # si le script est utilisé comme un module, on n'exécute pas le script

Nous voici à la fin de ce tuto... déjà !
Dans ce tutoriel, j'ai mis à disposition toutes mes connaissances sur ce module, et j'espère qu'elles vous conviendront. :)

Je tiens à remercier delroth qui a refait mon exemple d'utilisation dans la dernière partie (mais j'ai quand même écrit les commentaires :) ). Grâce à lui, vous avez un code qui va beaucoup mieux fonctionner. :)

Théoriquement, vous savez dorénavant utiliser cette librairie ! En cas de trou de mémoire, vous pouvez venir lire la partie 2 du tutoriel qui liste toutes les fonctions principales.


Toutes les fonctions, une à une