1

それで、私はファイルの大規模なサーバー(のディレクトリ)を多数の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]))

しかし、喜びはありません。

助けてくれてありがとう!

4

1 に答える 1

2

私の理解が正しければ、この種のことは で非常に簡単に実行できますmultiprocessing。1つのファイルをアップロードする関数を書くだけです -

例えば

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)

アップロードが速い場合は、チャンクサイズをいじることもできます。これにより、少し速度が上がります。

もちろん、ファイル名以外の情報を渡す必要がある場合は、ファイルのリストをタプルのリストにして、関数でアンパックするのが非常に簡単な方法です。

警告

基本的に副作用のためにマップ関数を使用しているため、この悪い習慣を考える人もいるかもしれません...

編集

あなたの問題は、Python の にp.map(self.upload_files(f), self.files_to_upload) 慣れてい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 でなければなりません。どうやらインスタンス メソッド (クラスのインスタンスにバインドされたメソッド) は pickleable ではありません。1 つの解決策は、通常の関数に変更することです。次のようにしてそれを行うことができます。

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

うまくいけば、うまくいくでしょう。幸運を!

于 2012-05-07T12:34:37.820 に答える