Cargue carpetas desde el sistema local a FTP utilizando el script Python

Tengo que cargar carpetas automáticamente en un FTP utilizando un script de Python. Puedo cargar un solo archivo, pero no carpetas con subcarpetas y archivos en ellas. Hice mucha búsqueda, pero fallé. ¿Podría alguien ayudarme aquí? Gracias por adelantado.

#! /usr/bin/python import ftplib s = ftplib.FTP('serverip','usrname','password') file = '/home/rock/test.txt' ftppath = '/IT' filename = "rak" s.cwd(ftppath) f = open(file,'rb') s.storbinary('STOR ' + filename, f) f.close() s.quit() 

Básicamente necesitas usar os.walk () para capturar esos archivos y transferirlos.

Aquí hay un guión que escribí para hacer mucho de lo que me pides. Lo escribí hace mucho tiempo, así que probablemente lo haría de manera diferente si lo escribiera de nuevo, pero le doy mucho uso.

Importa psftplib, que es un envoltorio que escribí para la masilla sftp. Siéntase libre de eliminar estas referencias, o busque la biblioteca en: http://code.google.com/p/psftplib/source/browse/trunk/psftplib.py

 # -*- coding: utf8 -*- '''This tool will ftp all the files in a given directory to a given location if the file ftpallcfg.py exists in the directory it will be loaded and the values within it used, with the current directory used as the source directory. ftpallcfg.py file contains the following variables. =========================== server =  username =  remote_dir =  encrypt= True/False monitor = True/False walk = True/False =========================== ''' import ftplib import os import getpass import sys import time import socket import psftplib __revision__ = 1.11 SLEEP_SECONDS = 1 class FtpAddOns(): PATH_CACHE = [] def __init__(self, ftp_h): self.ftp_h = ftp_h def ftp_exists(self, path): '''path exists check function for ftp handler''' exists = None if path not in self.PATH_CACHE: try: self.ftp_h.cwd(path) exists = True self.PATH_CACHE.append(path) except ftplib.error_perm, e: if str(e.args).count('550'): exists = False else: exists = True return exists def ftp_mkdirs(self, path, sep='/'): '''mkdirs function for ftp handler''' split_path = path.split(sep) new_dir = '' for server_dir in split_path: if server_dir: new_dir += sep + server_dir if not self.ftp_exists(new_dir): try: print 'Attempting to create directory (%s) ...' % (new_dir), self.ftp_h.mkd(new_dir) print 'Done!' except Exception, e: print 'ERROR -- %s' % (str(e.args)) def _get_local_files(local_dir, walk=False): '''Retrieve local files list result_list == a list of dictionaries with path and mtime keys. ex: {'path':,'mtime':} ignore_dirs == a list of directories to ignore, should not include the base_dir. ignore_files == a list of files to ignore. ignore_file_ext == a list of extentions to ignore. ''' result_list = [] ignore_dirs = ['CVS', '.svn'] ignore_files = ['.project', '.pydevproject'] ignore_file_ext = ['.pyc'] base_dir = os.path.abspath(local_dir) for current_dir, dirs, files in os.walk(base_dir): for this_dir in ignore_dirs: if this_dir in dirs: dirs.remove(this_dir) sub_dir = current_dir.replace(base_dir, '') if not walk and sub_dir: break for this_file in files: if this_file not in ignore_files and os.path.splitext(this_file)[-1].lower() not in ignore_file_ext: filepath = os.path.join(current_dir, this_file) file_monitor_dict = { 'path': filepath, 'mtime': os.path.getmtime(filepath) } result_list.append(file_monitor_dict) return result_list def monitor_and_ftp(server, username, password, local_dir, remote_dir, encrypt=False, walk=False): '''Monitor local files and when an update is found connect and upload''' print 'Monitoring changes in (%s).' % (os.path.abspath(local_dir)) print '(Use ctrl-c to exit)' last_files_list = _get_local_files(local_dir) while True: try: time.sleep(SLEEP_SECONDS) latest_files_list = _get_local_files(local_dir) files_to_update = [] for idx in xrange(len(latest_files_list)): if idx < len(last_files_list): # compare last modified times if latest_files_list[idx]['mtime'] > last_files_list[idx]['mtime']: files_to_update.append(latest_files_list[idx]) else: # add the file to the list (new file) files_to_update.append(latest_files_list[idx]) if files_to_update: print print 'Detected NEW or CHANGED file(s), attempting to send ...' print is_success = upload_all(server, username, password, local_dir, remote_dir, files_to_update, encrypt, walk) if not is_success: break else: print '.', last_files_list = latest_files_list[:] # copy the list to hold except KeyboardInterrupt: print print 'Exiting.' break def upload_all(server, username, password, base_local_dir, base_remote_dir, files_to_update=None, encrypt=False, walk=False): '''Upload all files in a given directory to the given remote directory''' continue_on = False login_ok = False server_connect_ok = False base_local_dir = os.path.abspath(base_local_dir) base_remote_dir = os.path.normpath(base_remote_dir) if files_to_update: local_files = files_to_update else: local_files = _get_local_files(base_local_dir, walk) if local_files: if not encrypt: # Use standard FTP ftp_h = ftplib.FTP() else: # Use sftp ftp_h = psftplib.SFTP() try: ftp_h.connect(server) server_connect_ok = True except socket.gaierror, e: print 'ERROR -- Could not connect to (%s): %s' % (server, str(e.args)) except IOError, e: print 'ERROR -- File not found: %s' % (str(e.args)) except socket.error, e: print 'ERROR -- Could not connect to (%s): %s' % (server, str(e.args)) ftp_path_tools = FtpAddOns(ftp_h) if server_connect_ok: try: ftp_h.login(username,password) print 'Logged into (%s) as (%s)' % (server, username) login_ok = True except ftplib.error_perm, e: print 'ERROR -- Check Username/Password: %s' % (str(e.args)) except psftplib.ProcessTimeout, e: print 'ERROR -- Check Username/Password (timeout): %s' % (str(e.args)) if login_ok: for file_info in local_files: filepath = file_info['path'] path, filename = os.path.split(filepath) remote_sub_path = path.replace(base_local_dir, '') remote_path = path.replace(base_local_dir, base_remote_dir) remote_path = remote_path.replace('\\', '/') # Convert to unix style if not ftp_path_tools.ftp_exists(remote_path): ftp_path_tools.ftp_mkdirs(remote_path) # Change to directory try: ftp_h.cwd(remote_path) continue_on = True except ftplib.error_perm, e: print 'ERROR -- %s' % (str(e.args)) except psftplib.PsFtpInvalidCommand, e: print 'ERROR -- %s' % (str(e.args)) if continue_on: if os.path.exists(filepath): f_h = open(filepath,'rb') filename = os.path.split(f_h.name)[-1] display_filename = os.path.join(remote_sub_path, filename) display_filename = display_filename.replace('\\', '/') print 'Sending (%s) ...' % (display_filename), send_cmd = 'STOR %s' % (filename) try: ftp_h.storbinary(send_cmd, f_h) f_h.close() print 'Done!' except Exception, e: print 'ERROR!' print str(e.args) print else: print "WARNING -- File no longer exists, (%s)!" % (filepath) ftp_h.quit() print 'Closing Connection' else: print 'ERROR -- No files found in (%s)' % (base_local_dir) return continue_on if __name__ == '__main__': import optparse default_config_file = u'ftpallcfg.py' # Create parser, and configure command line options to parse parser = optparse.OptionParser() parser.add_option("-l", "--local_dir", dest="local_dir", help="Local Directory (Defaults to CWD)", default='.') parser.add_option("-r", "--remote_dir", dest="remote_dir", help="[REQUIRED] Target Remote directory", default=None) parser.add_option("-u", "--username", dest="username", help="[REQUIRED] username", default=None) parser.add_option("-s","--server", dest="server", help="[REQUIRED] Server Address", default=None) parser.add_option("-e", "--encrypt", action="store_true", dest="encrypt", help="Use sftp", default=False) parser.add_option("-m", action="store_true", dest="monitor", help="Keep process open and monitor changes", default=False) parser.add_option("-w", action="store_true", dest="walkdir", help="Walk sub directories of the given directory to find files to send.", default=False) (options,args) = parser.parse_args() if (options.username and options.server and options.remote_dir) or \ os.path.exists(default_config_file): local_dir = options.local_dir if os.path.exists(default_config_file): sys.path.append('.') import ftpallcfg try: server = ftpallcfg.server username = ftpallcfg.username remote_dir = ftpallcfg.remote_dir encrypt = ftpallcfg.encrypt monitor = ftpallcfg.monitor walk = ftpallcfg.walk except AttributeError, e: print "ERROR --", str(e.args) print print 'Value(s) missing in %s file! The following values MUST be included:' % (default_config_file) print '================================' print 'server = ' print 'username = ' print 'remote_dir = ' print 'encrypt= True/False' print 'monitor = True/False' print 'walk == True/False' print '================================' sys.exit() else: server = options.server username = options.username remote_dir = options.remote_dir encrypt = options.encrypt monitor = options.monitor walk = options.walkdir # get the user password prompt = 'Password (%s@%s): ' % (username, server) if os.isatty(sys.stdin.fileno()): p = getpass.getpass(prompt) else: #p = sys.stdin.readline().rstrip() p = raw_input(prompt).rstrip() if options.encrypt: print '>> Using sftp for secure transfers <<' print if monitor: try: monitor_and_ftp(server,username,p,local_dir, remote_dir, encrypt, walk) except KeyboardInterrupt: print 'Exiting...' else: try: upload_all(server, username, p, local_dir, remote_dir, [], encrypt, walk) except KeyboardInterrupt: print 'Exiting...' else: print 'ERROR -- Required option not given!' print __revision__ print __doc__ print parser.print_help() 

Recientemente me encontré con este problema y descubrí una función recursiva para resolverlo.

 import ftplib import os server = 'localhost' username = 'generic_user' password = 'password' myFTP = ftplib.FTP(server, username, password) myPath = r'c:\temp' def uploadThis(path): files = os.listdir(path) os.chdir(path) for f in files: if os.path.isfile(path + r'\{}'.format(f)): fh = open(f, 'rb') myFTP.storbinary('STOR %s' % f, fh) fh.close() elif os.path.isdir(path + r'\{}'.format(f)): myFTP.mkd(f) myFTP.cwd(f) uploadThis(path + r'\{}'.format(f)) myFTP.cwd('..') os.chdir('..') uploadThis(myPath) # now call the recursive function 

EDITAR 20/12/2017:

He escrito un proyecto en GitHub para este propósito. Haga clic para más detalles!


Hay buenas respuestas arriba, pero también quiero agregar una buena usando el paquete ftputil . Si necesita cargar archivos del directorio local al directorio ftp, puede usar esta función recursiva:

 def upload_dir(localDir, ftpDir): list = os.listdir(localDir) for fname in list: if os.path.isdir(localDir + fname): if(ftp_host.path.exists(ftpDir + fname) != True): ftp_host.mkdir(ftpDir + fname) print(ftpDir + fname + " is created.") upload_dir(localDir + fname + "/", ftpDir + fname + "/") else: if(ftp_host.upload_if_newer(localDir + fname, ftpDir + fname)): print(ftpDir + fname + " is uploaded.") else: print(localDir + fname + " has already been uploaded.") 

Si decide utilizar esta función, debe conectar ftp utilizando el paquete ftputil. Para esto, puedes usar este fragmento de código:

 with ftputil.FTPHost("ftp_host", "ftp_username", "ftp_password") as ftp_host: 

Así que, casi hemos terminado. Lo último es el uso de la función para principiantes como yo:

 local_dir = "D:/Projects/.../" ftp_dir = "/.../../" upload_dir(local_dir, ftp_dir) 

Lo más importante es el carácter “/” al final de las rutas. Necesitas ponerlo al final. Finalmente, quiero compartir el código completo:

 with ftputil.FTPHost("ftp_host", "ftp_username", "ftp_password") as ftp_host: def upload_dir(localDir, ftpDir): list = os.listdir(localDir) for fname in list: if os.path.isdir(localDir + fname): if(ftp_host.path.exists(ftpDir + fname) != True): ftp_host.mkdir(ftpDir + fname) print(ftpDir + fname + " is created.") upload_dir(localDir + fname + "/", ftpDir + fname + "/") else: if(ftp_host.upload_if_newer(localDir + fname, ftpDir + fname)): print(ftpDir + fname + " is uploaded.") else: print(localDir + fname + " has already been uploaded.") local_dir = "D:/Projects/.../" ftp_dir = "/.../../" upload_dir(local_dir, ftp_dir) 

Tal vez intente ftpsync.py . Si esto no ayuda, intente la búsqueda de google en python ftpsync y obtendrá muchas respuestas.

utilizando ftputil :

 import os import ftputil import ftputil.session def upload_dir(root): root = unicode(root, 'utf-8') for dir_name, _, dir_files in os.walk(root): local = os.path.join(os.curdir, dir_name) remote = ftp_host.path.join(ftp_host.curdir, dir_name) if not ftp_host.path.exists(remote): print 'mkdir:', local, '->', remote ftp_host.mkdir(remote) for f in dir_files: local_f = os.path.join(local, f) remote_f = ftp_host.path.join(remote, f) print 'upload:', local_f, '->', remote_f ftp_host.upload(local_f, remote_f) sf = ftputil.session.session_factory(use_passive_mode=True) with ftputil.FTPHost('HOST', 'USER', 'PASS', session_factory=sf) as ftp_host: upload_dir('DIR') 

Es fácil usar lftp para cargar carpetas a un FTP. Lo uso en mi script de Python para mover carpetas a FTP

Script de Python: #! / usr / bin / python

 import subprocess subprocess.call(["bash", ftp_script, username, password, ftp, folder_to_move, src,folder_name_in_destination]) 

ftp_script:

 lftp -u $1,$2 $3 <