2010-04-09 2 views
7

Это не будет загружать содержимое подкаталогов; как я могу это сделать?Загрузка дерева каталогов с ftplib

import ftplib 
import configparser 
import os 

directories = [] 

def add_directory(line): 
if line.startswith('d'): 
    bits = line.split() 
    dirname = bits[8] 
    directories.append(dirname) 

def makeDir(archiveTo): 
for dir in directories: 
    newDir = os.path.join(archiveTo, dir) 
    if os.path.isdir(newDir) == True: 
    print("Directory \"" + dir + "\" already exists!") 
    else: 
    os.mkdir(newDir) 

def getFiles(archiveTo, ftp): 
files = ftp.nlst() 
for filename in files: 
    try: 
    directories.index(filename) 
    except: 
    ftp.retrbinary('RETR %s' % filename, open(os.path.join(archiveTo, filename), 'wb').write) 

def runBackups(): 

#Load INI 
filename = 'connections.ini' 
config = configparser.SafeConfigParser() 
config.read(filename) 

connections = config.sections() 
i = 0 

while i < len(connections): 
    #Load Settings 
    uri = config.get(connections[i], "uri") 
    username = config.get(connections[i], "username") 
    password = config.get(connections[i], "password") 
    backupPath = config.get(connections[i], "backuppath") 
    archiveTo = config.get(connections[i], "archiveto") 

    #Start Back-ups 
    ftp = ftplib.FTP(uri) 
    ftp.login(username, password) 
    ftp.cwd(backupPath) 

    #Map Directory Tree 
    ftp.retrlines('LIST', add_directory) 

    #Make Directories Locally 
    makeDir(archiveTo) 

    #Gather Files 
    getFiles(archiveTo, ftp) 

    #End connection and increase counter. 
    ftp.quit() 
    i += 1 

print() 
print("Back-ups complete.") 
print() 

ответ

6

это альтернатива. вы можете попробовать использовать пакет ftputil. Затем вы можете использовать его до walk the remote directories и получить ваши файлы

+0

Является ли это действительно так сложно, до точки, где я должен рассматривать пакеты? Я думаю, что это займет всего 10 строк действительно сложного кода. –

1

Это нетривиальный как минимум. В простейшем случае вы предполагаете, что у вас есть файлы и каталоги. Это не всегда так, есть программные ссылки и жесткие ссылки и ярлык в стиле Windows. Softlink и ярлык каталога особенно проблематичны, поскольку они делают возможной рекурсивную директорию, которая путала бы наивно реализованный ftp-захват.

Как бы вы справлялись с таким рекурсивным каталогом, зависит от ваших потребностей; вы можете просто не следовать программным ссылкам, или вы можете попытаться обнаружить рекурсивные ссылки. Обнаружение рекурсивной ссылки по своей сути сложно, вы не можете сделать это надежно.

10

это должно сделать трюк :)

import sys 
import ftplib 
import os 
from ftplib import FTP 
ftp=FTP("ftp address") 
ftp.login("user","password") 

def downloadFiles(path,destination): 
#path & destination are str of the form "/dir/folder/something/" 
#path should be the abs path to the root FOLDER of the file tree to download 
    try: 
     ftp.cwd(path) 
     #clone path to destination 
     os.chdir(destination) 
     os.mkdir(destination[0:len(destination)-1]+path) 
     print destination[0:len(destination)-1]+path+" built" 
    except OSError: 
     #folder already exists at destination 
     pass 
    except ftplib.error_perm: 
     #invalid entry (ensure input form: "/dir/folder/something/") 
     print "error: could not change to "+path 
     sys.exit("ending session") 

    #list children: 
    filelist=ftp.nlst() 

    for file in filelist: 
     try: 
      #this will check if file is folder: 
      ftp.cwd(path+file+"/") 
      #if so, explore it: 
      downloadFiles(path+file+"/",destination) 
     except ftplib.error_perm: 
      #not a folder with accessible content 
      #download & return 
      os.chdir(destination[0:len(destination)-1]+path) 
      #possibly need a permission exception catch: 
      ftp.retrbinary("RETR "+file, open(os.path.join(destination,file),"wb").write) 
      print file + " downloaded" 
    return 

source="/ftproot/folder_i_want/" 
dest="/systemroot/where_i_want_it/" 
downloadFiles(source,dest) 
+0

также вы можете не захотеть выйти в первом 'ftplib.error_perm', за исключением случаев, когда ваша учетная запись пользователя имеет разрешения. 'pass' мог бы работать. – jameh

6

Это очень старый вопрос, но у меня была подобная необходимость, что я хотел, чтобы удовлетворить в самом общем виде. В итоге я написал собственное решение, которое очень хорошо работает для меня. Я разместил его на Gist здесь https://gist.github.com/Jwely/ad8eb800bacef9e34dd775f9b3aad987

и наклеил его ниже, если я когда-нибудь возьму сумасшедший в автономном режиме.

Пример использования:

import ftplib 
ftp = ftplib.FTP(mysite, username, password) 
download_ftp_tree(ftp, remote_dir, local_dir) 

Код выше будет искать директорию под названием «remote_dir» на FTP хост, а затем дублировать каталог и все его содержимое в «local_dir». Он вызывает сценарий ниже.

import ftplib 
import os 

def _is_ftp_dir(ftp_handle, name, guess_by_extension=True): 
    """ simply determines if an item listed on the ftp server is a valid directory or not """ 

    # if the name has a "." in the fourth to last position, its probably a file extension 
    # this is MUCH faster than trying to set every file to a working directory, and will work 99% of time. 
    if guess_by_extension is True: 
     if name[-4] == '.': 
      return False 

    original_cwd = ftp_handle.pwd()  # remember the current working directory 
    try: 
     ftp_handle.cwd(name)   # try to set directory to new name 
     ftp_handle.cwd(original_cwd) # set it back to what it was 
     return True 
    except: 
     return False 


def _make_parent_dir(fpath): 
    """ ensures the parent directory of a filepath exists """ 
    dirname = os.path.dirname(fpath) 
    while not os.path.exists(dirname): 
     try: 
      os.mkdir(dirname) 
      print("created {0}".format(dirname)) 
     except: 
      _make_parent_dir(dirname) 


def _download_ftp_file(ftp_handle, name, dest, overwrite): 
    """ downloads a single file from an ftp server """ 
    _make_parent_dir(dest) 
    if not os.path.exists(dest) or overwrite is True: 
     with open(dest, 'wb') as f: 
      ftp_handle.retrbinary("RETR {0}".format(name), f.write) 
     print("downloaded: {0}".format(dest)) 
    else: 
     print("already exists: {0}".format(dest)) 


def _mirror_ftp_dir(ftp_handle, name, overwrite, guess_by_extension): 
    """ replicates a directory on an ftp server recursively """ 
    for item in ftp_handle.nlst(name): 
     if _is_ftp_dir(ftp_handle, item): 
      _mirror_ftp_dir(ftp_handle, item, overwrite, guess_by_extension) 
     else: 
      _download_ftp_file(ftp_handle, item, item, overwrite) 


def download_ftp_tree(ftp_handle, path, destination, overwrite=False, guess_by_extension=True): 
    """ 
    Downloads an entire directory tree from an ftp server to the local destination 

    :param ftp_handle: an authenticated ftplib.FTP instance 
    :param path: the folder on the ftp server to download 
    :param destination: the local directory to store the copied folder 
    :param overwrite: set to True to force re-download of all files, even if they appear to exist already 
    :param guess_by_extension: It takes a while to explicitly check if every item is a directory or a file. 
     if this flag is set to True, it will assume any file ending with a three character extension ".???" is 
     a file and not a directory. Set to False if some folders may have a "." in their names -4th position. 
    """ 
    os.chdir(destination) 
    _mirror_ftp_dir(ftp_handle, path, overwrite, guess_by_extension) 
+0

Awesome. Работает как шарм. Должна быть библиотека для этого! –

3

Использование ftputil, быстрое решение может быть:

def download(folder): 
    for item in ftp.walk(folder): 
     print("Creating dir " + item[0]) 
     os.mkdir(item[0]) 
     for subdir in item[1]: 
      print("Subdirs " + subdir) 
     for file in item[2]: 
      print(r"Copying File {0} \ {1}".format(item[0], file)) 
      ftp.download(ftp.path.join(item[0],file), os.path.join(item[0],file)) 
Смежные вопросы