Python FTP загружает несколько файлов одновременно
Итак, у меня есть сценарий, над которым я работаю для резервного копирования большого сервера (каталога) файлов на несколько учетных записей / служб 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 ответ
Если я вас правильно понимаю, такого рода вещи можно сделать довольно легко с multiprocessing
, просто напишите функцию для загрузки одного файла -
например
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)
Вы также можете поиграть с размером фрагмента, который немного ускорит процесс загрузки.
Конечно, если вам нужно передать больше информации, чем просто имя файла, один действительно простой способ сделать это - сделать список файлов списком кортежей и распаковать их в функции.
ПРЕДУПРЕЖДЕНИЕ
Некоторые могут счесть это плохой практикой, так как вы по существу используете функцию карты для побочных эффектов...
РЕДАКТИРОВАТЬ
Я думаю, что ваша проблема p.map(self.upload_files(f), self.files_to_upload)
Я не знаком с FTP
в Python, поэтому я не могу сказать наверняка, но вы хотите передать функцию в качестве первого параметра 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
, Все объекты, которые вы отправляете, должны быть замаринованными. Очевидно, ваш метод экземпляра (метод, связанный с экземпляром класса) не является маринованным. Одним из решений является то, что вы можете изменить его на обычную функцию. Вы можете сделать это следующим образом.
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()
Надеюсь, это сработает для вас. Удачи!