web-dev-qa-db-fra.com

Télécharger des fichiers à l'aide de SFTP en Python, mais créer des répertoires si le chemin n'existe pas

Je veux télécharger un fichier sur un serveur distant avec Python. J'aimerais vérifier au préalable si le chemin distant existe vraiment, et si ce n'est pas le cas, pour le créer. En pseudocode:

if(remote_path not exist):
    create_path(remote_path)
upload_file(local_file, remote_path)

Je pensais à l'exécution d'une commande dans Paramiko pour créer le chemin (par exemple mkdir -p remote_path). Je suis venu avec ceci:

# I didn't test this code

import paramiko, sys

ssh = paramiko.SSHClient()
ssh.connect(myhost, 22, myusername, mypassword)
ssh.exec_command('mkdir -p ' + remote_path)
ssh.close

transport = paramiko.Transport((myhost, 22))
transport.connect(username = myusername, password = mypassword)

sftp = paramiko.SFTPClient.from_transport(transport)
sftp.put(local_path, remote_path)
sftp.close()

transport.close()

Mais cette solution ne me semble pas satisfaisante, car je ferme la connexion puis la rouvre à nouveau. Y a-t-il une meilleure façon de le faire?

20
franzlorenzon

SFTP supporte les commandes FTP habituelles (chdir, mkdir, etc ...), utilisez donc celles-ci:

sftp = paramiko.SFTPClient.from_transport(transport)
try:
    sftp.chdir(remote_path)  # Test if remote_path exists
except IOError:
    sftp.mkdir(remote_path)  # Create remote_path
    sftp.chdir(remote_path)
sftp.put(local_path, '.')    # At this point, you are in remote_path in either case
sftp.close()

Pour émuler entièrement mkdir -p, vous pouvez travailler de façon récursive via remote_path:

import os.path

def mkdir_p(sftp, remote_directory):
    """Change to this directory, recursively making new folders if needed.
    Returns True if any folders were created."""
    if remote_directory == '/':
        # absolute path so change directory to root
        sftp.chdir('/')
        return
    if remote_directory == '':
        # top-level relative directory must exist
        return
    try:
        sftp.chdir(remote_directory) # sub-directory exists
    except IOError:
        dirname, basename = os.path.split(remote_directory.rstrip('/'))
        mkdir_p(sftp, dirname) # make parent directories
        sftp.mkdir(basename) # sub-directory missing, so created it
        sftp.chdir(basename)
        return True

sftp = paramiko.SFTPClient.from_transport(transport)
mkdir_p(sftp, remote_path) 
sftp.put(local_path, '.')    # At this point, you are in remote_path
sftp.close()

Bien sûr, si chemin_distant contient également un nom de fichier distant, il doit être séparé, le répertoire étant passé à mkdir_p et le nom de fichier utilisé à la place de '.'. dans sftp.put.

36
isedev

Quelque chose de plus simple et légèrement plus lisible aussi 

def mkdir_p(sftp, remote, is_dir=False):
    """
    emulates mkdir_p if required. 
    sftp - is a valid sftp object
    remote - remote path to create. 
    """
    dirs_ = []
    if is_dir:
        dir_ = remote
    else:
        dir_, basename = os.path.split(remote)
    while len(dir_) > 1:
        dirs_.append(dir_)
        dir_, _  = os.path.split(dir_)

    if len(dir_) == 1 and not dir_.startswith("/"): 
        dirs_.append(dir_) # For a remote path like y/x.txt 

    while len(dirs_):
        dir_ = dirs_.pop()
        try:
            sftp.stat(dir_)
        except:
            print "making ... dir",  dir_
            sftp.mkdir(dir_)
6
gabhijit

Je devais le faire aujourd'hui. Voici comment je l'ai fait.

def mkdir_p(sftp, remote_directory):
    dir_path = str()
    for dir_folder in remote_directory.split("/"):
        if dir_folder == "":
            continue
        dir_path += r"/{0}".format(dir_folder)
        try:
            sftp.listdir(dir_path)
        except IOError:
            sftp.mkdir(dir_path)
3
Mickey Afaneh
1
Benjamin

En supposant que les opérations sftp sont chères, .__ Je voudrais avec:

def sftp_mkdir_p(sftp, remote_directory):
    dirs_exist = remote_directory.split('/')
    dirs_make = []
    # find level where dir doesn't exist
    while len(dirs_exist) > 0:
        try:
            sftp.listdir('/'.join(dirs_exist))
            break
        except IOError:
            value = dirs_exist.pop()
            if value == '':
                continue
            dirs_make.append(value)
        else:
            return False
    # ...and create dirs starting from that level
    for mdir in dirs_make[::-1]:
        dirs_exist.append(mdir)
        sftp.mkdir('/'.join(dirs_exist))```
0
Daniil Burmashev