2013-02-11 8 views
15

Chcę przesłać plik na zdalny serwer za pomocą Pythona. Chciałbym wcześniej sprawdzić, czy ścieżka zdalna rzeczywiście istnieje, a jeśli nie, to ją utworzyć. W pseudokodzie:Przesyłaj pliki za pomocą SFTP w Pythonie, ale twórz katalogi, jeśli ścieżka nie istnieje

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

I myślał o wykonania polecenia w Paramiko utworzyć ścieżkę (np mkdir -p remote_path). Wpadłem na to:

# 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() 

Jednak rozwiązanie to nie brzmi dobrze dla mnie, bo zamknięcie połączenia i ponownie otworzyć go ponownie. Czy istnieje lepszy sposób na zrobienie tego?

+0

pokrewne: [os.renames dla FTP w python] (http://stackoverflow.com/q/14641267/4279) – jfs

Odpowiedz

26

SFTP obsługuje zwykłe polecenia FTP (chdir, mkdir, etc ...), więc korzystać z tych:

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() 

Aby w pełni emulować mkdir -p, można pracować z rekursywnie ścieżką zdalną:

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() 

Oczywiście, jeśli ścieżka_zdalna zawiera również nazwę pliku zdalnego, to należy ją odciąć, a katalog przekazać do mkdir_p i użyć nazwy pliku zamiast "." w sftp.put.

+0

nie obsługuje nieistniejących katalogów nadrzędnych ('-p'). Porównaj osmkdir() vs. os.makedirs(). Podziel ścieżkę i wykonaj wywołanie rekursywne, aby utworzyć katalogi nadrzędne, jeśli to konieczne. – jfs

+0

Tak, uzgodniono, zostanie odpowiednio zaktualizowana. – isedev

+0

w funkcji mkdir_p nie ma obsługi do sftp – franzlorenzon

5

Coś nieco prostsze i bardziej czytelne zbyt

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_) 
+0

+1 za udostępnienie nierekurencyjnej alternatywy. Zauważ, że "zdalny" parametr wejściowy jest tu zdalną ścieżką do pliku.Jeśli chcesz, aby ta funkcja miała zdalną ścieżkę do katalogu jako dane wejściowe, zamień "dir_, basename = os.path.split (zdalnie)" na "dir_ = zdalny". –

+0

@AlanEvangelista Dzięki za komentarz. Zaktualizowano kod, który przekazuje flagę 'is_dir'. W razie potrzeby przejrzyj i edytuj. – gabhijit

1

musiałem zrobić to dzisiaj. Oto jak to zrobiłem.

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)