2011-04-14 107 views
11

我必須使用Python腳本自動將文件夾上傳到FTP。我可以上傳單個文件,但不能包含子文件夾和其中的文件。我做了很多搜索,但失敗了。有人能幫我解決嗎?提前致謝。使用Python腳本將文件夾從本地系統上傳到FTP

#! /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() 
+0

你能提供你的代碼嗎? – Skurmedel 2011-04-14 13:15:26

+2

以及你將不得不遍歷文件和文件夾,並執行您的每個文件的工作代碼。你還期望如何做到這一點? – simon 2011-04-14 13:17:24

回答

8

您基本上需要使用os.walk()來抓取這些文件並將其傳輸。

這是我爲自己寫的一個腳本,用於處理你的問題。 我很久以前寫過這篇文章,所以如果我再寫一遍,可能會做得不一樣,但我從中得到了很多應用。

它導入psftplib,這是我爲膩子sftp編寫的包裝器。 隨時刪除這些引用,或搶LIB: 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 (%[email protected]%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() 
+2

找到。根據你的回答,我創建了一個小型獨立腳本:https://bitbucket.org/renficiaud/yayi/src/389f19e5701cabbae46dd7a51218b8ab044ab920/plugins/project_management/common/ftp_transfer.py?at = SWD-264 – Raffi 2015-06-29 13:23:33

+1

@Raffi您的鏈接已經死亡。 – m3h0w 2017-03-20 14:00:23

+0

更新後的鏈接[這裏](https://bitbucket.org/renficiaud/yayi/src/6797a73f86abb18a67abf298a6fd04e2b7ae6766/plugins/project_management/common/ftp_transfer.py?at=develop&fileviewer=file-view-default) – Raffi 2017-06-29 09:01:15

4

也許你試試ftpsync.py。如果這個沒有幫助,請嘗試谷歌搜索python ftpsync,你會得到很多答案。

+0

我在此處添加了「.py」鏈接,因爲已經有一個GNU'ftpsync',它是一個Perl腳本,可以在http://savannah.gnu.org/projects/ftpsync – sdaau 2014-03-02 19:08:08

2

這是很容易使用的lftp將文件夾上傳到FTP。我用我的Python腳本將文件夾移動到FTP

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 <<EOF 

mkdir $4 
lcd $5 
cd $6 
mirror --reverse 
EOF 
16

最近,我到這個問題,並想出了一個遞歸函數來解決這個問題。

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

最好的解決方案我可以發現,自包含和不依賴於其他模塊!不錯的工作 – timeyyy 2015-01-12 15:57:53

+4

不錯,一個小問題:使用'os.path.join'會確保你的代碼不依賴於操作系統。 – marcelka 2015-08-19 13:40:49

+0

如果遇到問題,請將所有路徑+ r'\ {}'。格式(f)更改爲f。這樣它對我有用 - 原來沒有。 – Taku 2016-04-14 09:16:57

2

使用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') 
4

編輯20/12/2017:

我已經在GitHub上寫了一個項目用於此目的。 Click for details!


上面有很好的答案,但我也想添加使用ftputil包好的。如果您需要從本地目錄文件上傳到FTP目錄,你可以使用這個遞歸函數:如果您決定使用此功能

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.") 

,你必須使用ftputil包連接FTP。爲此,您可以使用以下代碼片段:

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

所以,我們差不多完成了。最後一件事是像我這樣的初學者使用函數:

local_dir = "D:/Projects/.../" 
ftp_dir = "/.../../" 

upload_dir(local_dir, ftp_dir) 

最重要的是「/」字符在路徑的末尾。你需要把它放在最後。最後,我想分享整個代碼:

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) 
相關問題