2012-05-07 56 views
1

因此,我有一个脚本,我正在努力将大型服务器(的目录)的文件备份到大量的FTP帐户/服务/任何(目前穷人秘书有一个复制和粘贴文件来做到这一点,但无论如何,我已经接近有一个工作脚本来从她那里保存她= D)。我之前并没有真正地搞过线程或多处理,但我无法弄清楚如何让它获取文件列表,并且一次性向主机上传'em全部(in这个例子,我试着5,但我不知道我会决定什么)。Python FTP一次上传多个文件

import os, sys, subprocess, shutil, re, string, glob, tvdb_api, itertools, multiprocessing, ftplib 

files = [os.path.join(r, f) for r, d, fs in os.walk(os.getcwd()) for f in fs if not f[0]=='.'] 
class FTP_Upload: 
    def __init__(self, p=os.getcwd()): 
     self.files_to_upload = sorted([f for f in files if os.path.split(f)[0] == p]) 
     self.target = raw_input("Enter the host you want to upload to: ") 
     self.host = FTP('ftp.host1.com', 'user_name1', 'super_secret_password1') if self.target == 'host' else FTP('ftp.host2.com', 'user_name2', 'secret_password2') if self.target == 'host2' else None 
    def upload_files(self, f): 
     self.host.storbinary(('STOR /'+f.split('/')[-1]), open(f, 'rb')) 
    def multiupload(self): 
     p = multiprocessing.Pool(processes=5) 
     p.map(self.upload_files(f), self.files_to_upload) 
FTP_Upload().multiupload() 

但是,这只是在上传的self.files_to_upload最后一个文件...

我尝试了制作文件列表中的迭代

self.files_to_upload = iter(sorted([f for f in files if os.path.split(f)[0] == p])) 

,但没有喜悦。

在此先感谢您的帮助!

+2

问题在哪里?你知道有一个[ftplib](http://docs.python.org/library/ftplib.html#module-ftplib)? – dav1d

+0

哈哈,对不起,当我不睡觉的时候,我会变得更加懒散,并且ADD。我应该远离自己的键盘,但我从来没有那么聪明。 –

回答

2

如果我理解正确,可以用multiprocessing轻松完成此类事情。只需编写上传一个文件的功能 -

例如

def upload_one(filename): 
    """ This function uploads one file. 
     Perhaps is a a wrapper to your Popen call? """ 

然后用mulitprocessing文件

mylistoffiles=[ ] #Somehow generate your list of files to be uploaded. 
import multiprocessing 
Pool=multiprocessing.Pool(processes=X) #X is the number of processes you want to use 
Pool.map(upload_one,mylistoffiles) 

的名单还可以玩的CHUNKSIZE如果上传很快,这将加快东西一点点。

当然,如果你需要传递更多的信息而不仅仅是文件名,一个非常简单的方法就是让你的文件列表成为一个元组列表并将它们解压缩到函数中。

警告

因为你基本上使用副作用的地图功能,有些人可能会认为这是不好的做法...

编辑

我觉得你的问题是p.map(self.upload_files(f), self.files_to_upload) 我不熟悉Python中的FTP,所以我不能肯定地说,但是你想通过一个函数作为第一个参数为p.map。你传递函数的输出 - 你可能写了一个返回函数的函数,但它看起来不像上面的代码。

你可能想要的是:

p.map(self.upload_files,self.files_to_upload) 

在一般情况下,一个map函数的调用可以如下转化为一个列表理解:

map(function,iterable) 

几乎等同于

[function(i) for i in iterable] 

(几乎相同,因为在python3.x中map返回一个发电机。请注意,在map中,实际上并没有调用该函数。

最后编辑(希望)

您正在运行到的multiprocessing的(不幸的)限制。您在附近发送的所有对象必须是可剔除的。显然你的实例方法(绑定到一个类的实例的方法)是不可pickleable的。一个解决方案是,您可以将其更改为常规功能。你可以这样做,如下所示。

import os, sys, subprocess, shutil, re, string, glob, tvdb_api, itertools, multiprocessing, ftplib 

#No longer an instance method -- just a regular function. 
#accepts an iterable and then splits it as [host,filename] 
def upload_files(inpt): 
    host=inpt[0] 
    f=inpt[1] 
    #host,f=inpt #This might be a little cleaner, depending on your programming style. 
    host.storbinary(('STOR /'+f.split('/')[-1]), open(f, 'rb')) 

files = [os.path.join(r, f) for r, d, fs in os.walk(os.getcwd()) for f in fs if not f[0]=='.'] 
class FTP_Upload: 
    def __init__(self, p=os.getcwd()): 
     self.files_to_upload = sorted([f for f in files if os.path.split(f)[0] == p]) 
     self.target = raw_input("Enter the host you want to upload to: ") 
     self.host = FTP('ftp.host1.com', 'user_name1', 'super_secret_password1') if self.target == 'host' else FTP('ftp.host2.com', 'user_name2', 'secret_password2') if self.target == 'host2' else None 
    def multiupload(self): 
     p = multiprocessing.Pool(processes=5) 
     upload_this=[(self.host,f) for f in self.files_to_upload] 
     p.map(upload_files,upload_this) 
FTP_Upload().multiupload() 

希望能为你解决。祝你好运!

+0

@RobinHood看到我上面的编辑。我想你已经差不多了。 – mgilson

+0

谢谢,但是当我从multipupload中删除“(f)”,但是现在我得到了'Traceback(最近调用最后一个): 文件“/usr/lib/python2.7/threading.py”,第552行, in __bootstrap_inner self.run() 文件“/usr/lib/python2.7/threading.py”,行505,运行中 self .__ target(* self .__ args,** self .__ kwargs) File“/ usr/lib/python2.7/multiprocessing/pool.py“,行315,在_handle_tasks put(任务) PicklingError:无法pickle :属性查找__builtin __。instancemethod失败 –

+0

@RobinHood I'再次编辑。正如你的回溯表明的那样,你不能腌制一个实例方法。将其更改为常规功能,并希望您能全部设置好。 – mgilson