问题描述
我必须使用 Python 脚本自动将文件夹上传到 FTP.我可以上传单个文件,但不能上传带有子文件夹和文件的文件夹.我做了很多搜索,但失败了.有人可以帮我吗?提前致谢.
I have to automatically upload folders to an FTP using a Python script. I am able to upload a single file, but not folders with subfolders and files in them. I did a lot of search, but failed. Could some one help me out here? Thanks in advance.
#! /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()
推荐答案
你基本上需要使用 os.walk() 来抓取这些文件并传输它们.
You basically need to use os.walk() to grab those files and transfer them.
这是我为自己编写的脚本,用于满足您的大部分要求.我很久以前写的,所以如果我再写一次,我可能会做不同的事情,但我从中得到了很多用处.
Here's a script I wrote for myself to do much of what your asking. I wrote it a long time ago, so I'd probably do it differently if I wrote it again, but I get a lot of use out of it.
它导入 psftplib,这是我为 putty sftp 编写的包装器.随意删除这些引用,或在以下位置获取库:http://code.google.com/p/psftplib/source/browse/trunk/psftplib.py
It imports psftplib, which is a wrapper I wrote for the putty sftp. Feel free to remove these references, or grab the lib at: 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 = <server to ftp to> username = <Username for access to given server> remote_dir = <remote server directory> 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':<filepath>,'mtime':<file last modified time>} 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 = <server to ftp to>' print 'username = <Username for access to given server>' print 'remote_dir = <remote server directory>' 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()