17

Python requests パッケージを使用して大きなファイルをアップロードしていますが、アップロードの進行状況に関するデータを返す方法が見つかりません。ファイルをダウンロードするための進行状況メーターをいくつか見ましたが、ファイルのアップロードでは機能しません。

理想的な解決策は、次のようなコールバック メソッドです。

def progress(percent):
  print percent
r = requests.post(URL, files={'f':hugeFileHandle}, callback=progress)

よろしくお願いします:)

4

8 に答える 8

18

requests アップロード ストリーミングをサポートしていません。例:

import os
import sys
import requests  # pip install requests

class upload_in_chunks(object):
    def __init__(self, filename, chunksize=1 << 13):
        self.filename = filename
        self.chunksize = chunksize
        self.totalsize = os.path.getsize(filename)
        self.readsofar = 0

    def __iter__(self):
        with open(self.filename, 'rb') as file:
            while True:
                data = file.read(self.chunksize)
                if not data:
                    sys.stderr.write("\n")
                    break
                self.readsofar += len(data)
                percent = self.readsofar * 1e2 / self.totalsize
                sys.stderr.write("\r{percent:3.0f}%".format(percent=percent))
                yield data

    def __len__(self):
        return self.totalsize

# XXX fails
r = requests.post("http://httpbin.org/post",
                  data=upload_in_chunks(__file__, chunksize=10))

ところで、進捗状況を報告する必要がない場合。メモリ マップト ファイルを使用して大きなファイルをアップロードできます

これを回避するには、 urllib2 POST 進行状況監視のようなファイル アダプターを作成します 。

class IterableToFileAdapter(object):
    def __init__(self, iterable):
        self.iterator = iter(iterable)
        self.length = len(iterable)

    def read(self, size=-1): # TBD: add buffer for `len(data) > size` case
        return next(self.iterator, b'')

    def __len__(self):
        return self.length

it = upload_in_chunks(__file__, 10)
r = requests.post("http://httpbin.org/post", data=IterableToFileAdapter(it))

# pretty print
import json
json.dump(r.json, sys.stdout, indent=4, ensure_ascii=False)
于 2012-12-17T09:01:14.977 に答える
14

requests-toolbeltという名前のツール パッケージを使用することをお勧めします。これにより、アップロード バイトの監視が非常に簡単になります。

from requests_toolbelt import MultipartEncoder, MultipartEncoderMonitor
import requests

def my_callback(monitor):
    # Your callback function
    print monitor.bytes_read

e = MultipartEncoder(
    fields={'field0': 'value', 'field1': 'value',
            'field2': ('filename', open('file.py', 'rb'), 'text/plain')}
    )
m = MultipartEncoderMonitor(e, my_callback)

r = requests.post('http://httpbin.org/post', data=m,
                  headers={'Content-Type': m.content_type})

プログレスバーを表示するには、これを読んでください。

于 2015-02-23T13:06:31.793 に答える
10

ここからのコードで動作するようになりました: Simple file upload progressbar in PyQt。StringIO の代わりに BytesIO を使用するように、少し変更しました。

class CancelledError(Exception):
    def __init__(self, msg):
        self.msg = msg
        Exception.__init__(self, msg)

    def __str__(self):
        return self.msg

    __repr__ = __str__

class BufferReader(BytesIO):
    def __init__(self, buf=b'',
                 callback=None,
                 cb_args=(),
                 cb_kwargs={}):
        self._callback = callback
        self._cb_args = cb_args
        self._cb_kwargs = cb_kwargs
        self._progress = 0
        self._len = len(buf)
        BytesIO.__init__(self, buf)

    def __len__(self):
        return self._len

    def read(self, n=-1):
        chunk = BytesIO.read(self, n)
        self._progress += int(len(chunk))
        self._cb_kwargs.update({
            'size'    : self._len,
            'progress': self._progress
        })
        if self._callback:
            try:
                self._callback(*self._cb_args, **self._cb_kwargs)
            except: # catches exception from the callback
                raise CancelledError('The upload was cancelled.')
        return chunk


def progress(size=None, progress=None):
    print("{0} / {1}".format(size, progress))


files = {"upfile": ("file.bin", open("file.bin", 'rb').read())}

(data, ctype) = requests.packages.urllib3.filepost.encode_multipart_formdata(files)

headers = {
    "Content-Type": ctype
}

body = BufferReader(data, progress)
requests.post(url, data=body, headers=headers)

秘訣は、urllib3 の encode_multipart_formdata() を使用して、ファイル リストから手動でデータとヘッダーを生成することです。

于 2013-02-19T08:38:41.233 に答える
2

通常、チャンクされたファイルを読み取り、途中で進行状況を報告するストリーミング データソース (ジェネレーター) を構築します ( kennethreitz/requests#663を参照してください。これは、要求ファイル API では機能しません。要求はストリーミング アップロードをサポートしていないためです ( kennethreitz/requests#295を参照) – アップロードするファイルは、処理を開始する前にメモリ内で完了する必要があります。

ただし、JF Sebastian が以前に証明したように、要求はジェネレーターからコンテンツをストリーミングできますが、このジェネレーターは、マルチパート エンコーディングと境界を含む完全なデータストリームを生成する必要があります。これがポスターの出番です。

poster はもともとpythons urllib2で使用するために書かれており、マルチパート リクエストのストリーミング生成をサポートし、進行状況を示します。Posters Homepage は、urllib2 と一緒に使用する例を提供していますが、実際には urllib2 を使用したくありません。urllib2 を使用して HTTP 基本認証を行う方法については、このサンプル コードを確認してください。恐ろしい。

したがって、進行状況を追跡してファイルのアップロードを行うリクエストと一緒に poster を使用したいと考えています。方法は次のとおりです。

# load requests-module, a streamlined http-client lib
import requests

# load posters encode-function
from poster.encode import multipart_encode



# an adapter which makes the multipart-generator issued by poster accessable to requests
# based upon code from http://stackoverflow.com/a/13911048/1659732
class IterableToFileAdapter(object):
    def __init__(self, iterable):
        self.iterator = iter(iterable)
        self.length = iterable.total

    def read(self, size=-1):
        return next(self.iterator, b'')

    def __len__(self):
        return self.length

# define a helper function simulating the interface of posters multipart_encode()-function
# but wrapping its generator with the file-like adapter
def multipart_encode_for_requests(params, boundary=None, cb=None):
    datagen, headers = multipart_encode(params, boundary, cb)
    return IterableToFileAdapter(datagen), headers



# this is your progress callback
def progress(param, current, total):
    if not param:
        return

    # check out http://tcd.netinf.eu/doc/classnilib_1_1encode_1_1MultipartParam.html
    # for a complete list of the properties param provides to you
    print "{0} ({1}) - {2:d}/{3:d} - {4:.2f}%".format(param.name, param.filename, current, total, float(current)/float(total)*100)

# generate headers and gata-generator an a requests-compatible format
# and provide our progress-callback
datagen, headers = multipart_encode_for_requests({
    "input_file": open('recordings/really-large.mp4', "rb"),
    "another_input_file": open('recordings/even-larger.mp4', "rb"),

    "field": "value",
    "another_field": "another_value",
}, cb=progress)

# use the requests-lib to issue a post-request with out data attached
r = requests.post(
    'https://httpbin.org/post',
    auth=('user', 'password'),
    data=datagen,
    headers=headers
)

# show response-code and -body
print r, r.text
于 2014-03-14T15:28:36.817 に答える