2010-12-10 27 views

Respuesta

4

Tendrás que hacer esto como lo harías a nivel local con python (si no estuvieras usando shutils).

Combine os.walk(), con sftp.mkdir() y sftp.put(). También es posible que desee comprobar cada archivo y directorio con dependiendo de si desea resolver enlaces simbólicos o no.

2

No creo que se puede hacer eso. Busque la documentación para os.walk y copie cada archivo "manualmente".

0

Por lo que sé, Paramiko no admite la carga de archivo recursivo . Sin embargo, he encontrado un solution for recursive upload using Paramiko here. Sigue un extracto de su función de carga recursiva:

def _send_recursive(self, files): 
     for base in files: 
      lastdir = base 
      for root, dirs, fls in os.walk(base): 
       # pop back out to the next dir in the walk 
       while lastdir != os.path.commonprefix([lastdir, root]): 
        self._send_popd() 
        lastdir = os.path.split(lastdir)[0] 
       self._send_pushd(root) 
       lastdir = root 
       self._send_files([os.path.join(root, f) for f in fls]) 

Usted puede tratar de usar ya sea su función SCPClient.put invocando la función anterior para la carga recursiva o implementar por su cuenta.

+0

os.walk() es la forma correcta de hacerlo, pero no copie esto exactamente, ya que maneja las cosas de una manera específica para SCP. SFTP funciona de manera un poco diferente (exención de responsabilidad, escribí ese código) – JimB

+0

@Martin Kosek - Me gusta su respuesta, pero parece que su enlace a la solución está roto. ¿Podrías editar y arreglar? Gracias. – RobertMS

+0

@RobertMS - a la derecha, veo que se eliminó el módulo de Python. En este caso, creo que la solución de JimB sería la mejor combinación de os.walk(), sftp.mkdir() y sftp.put() para lograr el objetivo. –

3

Puede reemplazar sftp = self.client.open_sftp() con paramiko y deshacerse de libcloud aquí.

import os.path 
from stat import S_ISDIR 
from libcloud.compute.ssh import SSHClient 
from paramiko.sftp import SFTPError 

class CloudSSHClient(SSHClient): 


    @staticmethod 
    def normalize_dirpath(dirpath): 
     while dirpath.endswith("/"): 
      dirpath = dirpath[:-1] 
     return dirpath 


    def mkdir(self, sftp, remotepath, mode=0777, intermediate=False): 
     remotepath = self.normalize_dirpath(remotepath) 
     if intermediate: 
      try: 
       sftp.mkdir(remotepath, mode=mode) 
      except IOError, e: 
       self.mkdir(sftp, remotepath.rsplit("/", 1)[0], mode=mode, 
          intermediate=True) 
       return sftp.mkdir(remotepath, mode=mode) 
     else: 
      sftp.mkdir(remotepath, mode=mode) 


    def put_dir_recursively(self, localpath, remotepath, preserve_perm=True): 
     "upload local directory to remote recursively" 

     assert remotepath.startswith("/"), "%s must be absolute path" % remotepath 

     # normalize 
     localpath = self.normalize_dirpath(localpath) 
     remotepath = self.normalize_dirpath(remotepath) 

     sftp = self.client.open_sftp() 

     try: 
      sftp.chdir(remotepath) 
      localsuffix = localpath.rsplit("/", 1)[1] 
      remotesuffix = remotepath.rsplit("/", 1)[1] 
      if localsuffix != remotesuffix: 
       remotepath = os.path.join(remotepath, localsuffix) 
     except IOError, e: 
      pass 

     for root, dirs, fls in os.walk(localpath): 
      prefix = os.path.commonprefix([localpath, root]) 
      suffix = root.split(prefix, 1)[1] 
      if suffix.startswith("/"): 
       suffix = suffix[1:] 

      remroot = os.path.join(remotepath, suffix) 

      try: 
       sftp.chdir(remroot) 
      except IOError, e: 
       if preserve_perm: 
        mode = os.stat(root).st_mode & 0777 
       else: 
        mode = 0777 
       self.mkdir(sftp, remroot, mode=mode, intermediate=True) 
       sftp.chdir(remroot) 

      for f in fls: 
       remfile = os.path.join(remroot, f) 
       localfile = os.path.join(root, f) 
       sftp.put(localfile, remfile) 
       if preserve_perm: 
        sftp.chmod(remfile, os.stat(localfile).st_mode & 0777) 
+0

Respuesta bonita y completa. Solo algunos puntos menores: recomendaría usar 'os.path.split' en lugar de' str.rsplit'; también, defines un método 'normalize_path', pero luego haces' sufijo = sufijo [1:] 'en' put_dir_recursively'. –

9

Usted puede subclase paramiko.SFTPClient y añadir el siguiente método a la misma:

import paramiko 
import os 

class MySFTPClient(paramiko.SFTPClient): 
    def put_dir(self, source, target): 
     ''' Uploads the contents of the source directory to the target path. The 
      target directory needs to exists. All subdirectories in source are 
      created under target. 
     ''' 
     for item in os.listdir(source): 
      if os.path.isfile(os.path.join(source, item)): 
       self.put(os.path.join(source, item), '%s/%s' % (target, item)) 
      else: 
       self.mkdir('%s/%s' % (target, item), ignore_existing=True) 
       self.put_dir(os.path.join(source, item), '%s/%s' % (target, item)) 

    def mkdir(self, path, mode=511, ignore_existing=False): 
     ''' Augments mkdir by adding an option to not fail if the folder exists ''' 
     try: 
      super(MySFTPClient, self).mkdir(path, mode) 
     except IOError: 
      if ignore_existing: 
       pass 
      else: 
       raise 
2

Obras para mí hacer algo como esto, todas las carpetas y los archivos se copian en el servidor remoto.

parent = os.path.expanduser("~") 
for dirpath, dirnames, filenames in os.walk(parent): 
    remote_path = os.path.join(remote_location, dirpath[len(parent)+1:]) 
     try: 
      ftp.listdir(remote_path) 
     except IOError: 
      ftp.mkdir(remote_path) 

     for filename in filenames: 
      ftp.put(os.path.join(dirpath, filename), os.path.join(remote_path, filename)) 
2

Aquí es mi trozo de código:

import errno 
import os 
import stat 

def download_files(sftp_client, remote_dir, local_dir): 
    if not exists_remote(sftp_client, remote_dir): 
     return 

    if not os.path.exists(local_dir): 
     os.mkdir(local_dir) 

    for filename in sftp_client.listdir(remote_dir): 
     if stat.S_ISDIR(sftp_client.stat(remote_dir + filename).st_mode): 
      # uses '/' path delimiter for remote server 
      download_file(sftp_client, remote_dir + filename + '/', os.path.join(local_dir, filename)) 
     else: 
      if not os.path.isfile(os.path.join(local_dir, filename)): 
       sftp_client.get(remote_dir + filename, os.path.join(local_dir, filename)) 


def exists_remote(sftp_client, path): 
    try: 
     sftp_client.stat(path) 
    except IOError, e: 
     if e.errno == errno.ENOENT: 
      return False 
     raise 
    else: 
     return True 
0

Ésta es mi primera respuesta StackOverflow. Tuve una tarea hoy que es similar a esto. Entonces, traté de encontrar una forma directa de copiar toda la carpeta de Windows a Linux usando Python y Paramiko. Después de investigar un poco, se me ocurrió esta solución que funciona para carpetas de menor tamaño con subcarpetas y archivos.

Esta solución primero crea el archivo zip para la carpeta actual (os.walk() es muy útil aquí), luego lo copia al servidor de destino y descomprime allí.

Cuestiones relacionadas