Python FTP一次上传多个文件

时间:2012-05-07 12:04:19

标签: python ftp rar

所以,我有一个脚本,我正在努力将大型服务器(目录)的文件备份到许多FTP帐户/服务/任何东西(此时可怜的秘书有副本 - 和 - 粘贴文件来做到这一点,但无论如何我接近有一个工作脚本来保存她= D)。

我之前没有真正搞过线程或多处理,但是我无法弄清楚如何让它获取文件列表并一次将所有文件上传到主机3-5(在此例如,我正在尝试5,但我不知道我将决定什么。)

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]))

但没有快乐。

提前感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

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

e.g。

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

然后在文件列表中使用多重处理

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()

希望这对您有用。祝你好运!