Python:SimpleFTP
From Devicenull's Code
This is a simple wrapper around the Python:ReliableFTP class that provides some common FTP operations
import urlparse, os, sys, re from ftplib import FTP from reliableftp import ReliableFTP class FTPFileListProtocol: # stolen from twisted, who knows what licenses I am breaking fileLinePattern = re.compile( r'^(?P<filetype>.)(?P<perms>.{9})\s+(?P<nlinks>\d*)\s*' r'(?P<owner>\S+)\s+(?P<group>\S+)\s+(?P<size>\d+)\s+' r'(?P<date>...\s+\d+\s+[\d:]+)\s+(?P<filename>([^ ]|\\ )*?)' r'( -> (?P<linktarget>[^\r]*))?\r?$' ) delimiter = '\n' def parseDirectoryLine(self, line): """Return a dictionary of fields, or None if line cannot be parsed. @param line: line of text expected to contain a directory entry @type line: str @return: dict """ match = self.fileLinePattern.match(line) if match is None: return None else: d = match.groupdict() d['filename'] = d['filename'].replace(r'\ ', ' ') d['nlinks'] = int(d['nlinks']) d['size'] = int(d['size']) if d['linktarget']: d['linktarget'] = d['linktarget'].replace(r'\ ', ' ') return d valid_commands = ['S','O','A'] class SimpleFTP: def __init__(self,url): u = urlparse.urlparse(url) if u[0] != 'ftp': print "This isn't a ftp URL" exit fullhost = u[1].split('@',1) user = fullhost[0].split(':',1)[0] password = fullhost[0].split(':',1)[1] host = fullhost[1] self.ftp = ReliableFTP(host,user,password) #self.ftp.set_debuglevel(1) def ls(self,dest_dir=""): orig_dir = self.ftp.pwd() if dest_dir != "": self.ftp.cwd(dest_dir) lines = [] def appendlines(curline): lines.append(curline) files = self.ftp.retrlines("LIST",appendlines) files = [] flist = FTPFileListProtocol() for c in lines: d = flist.parseDirectoryLine(c) if d == None: continue cur_item = d['filename'] cur_type = d['filetype'][0] files.append((cur_type,cur_item)) self.ftp.cwd(orig_dir) return files # This callback occurs when a file already exists.. the default method is to ask the user # Must return (S)kip, (O)verwrite, (A)bort @staticmethod def onExists(filename): print "File %s already exists" % (filename) inp = raw_input("What should I do? Skip, Overwrite, Abort (S|O|A) ") while (valid_commands.count(inp) == 0): inp = raw_input("What should I do? Skip, Overwrite, Abort (S|O|A) ") return inp def uploadDirectory(self,source_dir,dest_dir,onExistsHandler=onExists): orig_dir = self.ftp.pwd() src_len = len(source_dir) for root, dirs, files in os.walk(source_dir): current_src_dir = root[src_len:].replace("\\","/") #print "R: %s D: %s F: %s" % (root, dirs, files) if (len(root) == src_len): #print 'In root directory' self.ftp.cwd(dest_dir) else: #print 'In directory: %s' % current_src_dir self.ftp.cwd("%s%s" % (dest_dir,current_src_dir)) for cur_dir in dirs: try: self.ftp.mkd(cur_dir) except: pass file_list = self.ls() for cur_file in files: exists = 0 for cur_type, filename in file_list: if (filename == cur_file): exists = 1 break inp = "" if exists == 1: inp = onExistsHandler("%s/%s" % (current_src_dir,cur_file)) if inp == 'A': print 'Aborting..' sys.exit(1) elif inp == 'O' or exists == 0: f = open("%s/%s" % (root,cur_file),"rb") if cur_file[0] == '\\' or cur_file[0] == '/': cur_file = cur_file[1:] self.ftp.storbinary("STOR %s" % cur_file,f) f.close() self.ftp.cwd(orig_dir) def uploadFile(self,dest_dir,dest_name,file_obj): orig_dir = self.ftp.pwd() self.ftp.cwd(dest_dir) self.ftp.storbinary("STOR %s" % dest_name,file_obj) self.ftp.cwd(orig_dir) def lookForFile(self,filename,ignoremasks=[]): return self.lookForObject(filename,ignoremasks,'F') def lookForDirectory(self,filename,ignoremasks=[]): return self.lookForObject(filename,ignoremasks,'D') # Generic version of lookForWhatever, 'F' for file, 'D' for directory def lookForObject(self,filename,ignoremasks,obj_type='F'): try: orig_dir = self.ftp.pwd() res = "" files = self.ls() directories = [] for cur_type, cur_item in files: if obj_type == 'F': if cur_type == 'd': directories.append(cur_item) else: #print '* File: %s' % cur_item if cur_item == filename: skip = 0 for c in ignoremasks: if orig_dir.find(c) != -1: skip = 1 if skip == 0: location = self.ftp.pwd() self.ftp.cwd(orig_dir) return location else: if cur_type == 'd': if cur_item == filename: skip = 0 for c in ignoremasks: if orig_dir.find(c) != -1: skip = 1 if skip == 0: location = self.ftp.pwd() self.ftp.cwd(orig_dir) return location # ok, it wasn't in this directory, keep looking! for c in directories: skip = 0 for ig in ignoremasks: if c.find(ig) != -1: skip = 1 if skip == 1: continue print 'Entering directory %s' % (c) self.ftp.cwd(c) res = self.lookForObject(filename,ignoremasks,obj_type) self.ftp.cwd(orig_dir) if res != "": return res self.ftp.cwd(orig_dir) return "" except Exception, e: print 'Original directory: %s' % orig_dir print 'Current directory: %s' % self.ftp.pwd() raise def downloadFile(self,src_dir,src_file,dest_obj): orig_dir = self.ftp.pwd() self.ftp.cwd(src_dir) self.ftp.retrbinary("RETR %s" % src_file, dest_obj.write) self.ftp.cwd(orig_dir)