urllib2.urlopen(request)や
requests.post()の形式で投げることになりますが、f.read()のような形で、引数としてファイル全体を与えてしまった場合、アップロードが完了するまで待たなければなりません。そのため、大きなファイルのアップロードなどでは特に、本当にアップロードが進んでいるのか知ることができず不便でした。
そこで、StringIOを使ってファイルをchunkに分割してロードできるようにし、read()が走るたびにコールバックを呼ぶようにしてやることで、アップロード中の進捗状況を知ることができるようになります。こちらを参考にしてStringIOを継承してコールバック関数を呼ぶためには、以下のように書けます。
from StringIO import StringIO
class BufferReader(StringIO):
def __init__(self, buf='',
callback=None,
cb_args=(), cb_kwargs={}):
self._callback = callback
self._cli_manager = cli_manager
self._progress = 0
self._cb_args = cb_args
self._cb_kwargs = cb_kwargs
StringIO.__init__(self, buf)
def __len__(self):
return self.len
def read(self, n=-1):
chunk = StringIO.read(self, n)
self._progress += int(len(chunk))
self._cb_kwargs.update({'progress': self._progress})
if self._callback:
try:
self._callback(*self._cb_args, **self._cb_kwargs)
except Exception, e: # catches exception from the callback
raise BaseException, e
これで、
req = urllib2.Request(url, BufferReader(f.read(), callback=callback_func)) res = urllib2.urlopen(req)とすることでcallback_funcにファイルアップロードの進捗を渡すことが可能になります。
ただし、multipartによるPOSTでファイルをアップロードする場合には、requests.post(files={'file': (filename, BufferReader(filebuffer))})の形式ではStringIOを使っても上記のread()が正しくchunkごとに呼ばれないようです。この場合はurllib3のencode_multipart_formdata()を使ってあらかじめmultipart用のデータを作り、この結果をBufferReaderに噛ませたあとrequests.post(files=)に入れてやることでアップロードするなどの方法をとる必要があります。
0 件のコメント:
コメントを投稿