urllib2 Мониторинг выполнения после публикации

#python #http #urllib2 #http-post

#python #http #urllib2 #http-post

Вопрос:

Я загружаю довольно большой файл с urllib2 в скрипт на стороне сервера через POST. Я хочу отобразить индикатор выполнения, который показывает текущий ход загрузки. Есть ли перехват или обратный вызов, предоставляемые urllib2, которые позволяют мне отслеживать ход загрузки? Я знаю, что вы можете сделать это с помощью download, используя последовательные вызовы метода read () соединения, но я не вижу метода write (), вы просто добавляете данные в запрос.

Ответ №1:

Это возможно, но вам нужно сделать несколько вещей:

  • Подделать подсистему urllib2 для передачи дескриптора файла в httplib, прикрепив __len__ атрибут, который заставляет len(data) возвращать правильный размер, используемый для заполнения заголовка Content-Length.
  • Переопределите read() метод в вашем дескрипторе файла: при вызовах httplib read() будет вызван ваш обратный вызов, позволяющий рассчитать процент и обновить индикатор выполнения.

Это может работать с любым файлоподобным объектом, но я обернул file , чтобы показать, как это может работать с действительно большим файлом, передаваемым с диска:

 import os, urllib2
from cStringIO import StringIO

class Progress(object):
    def __init__(self):
        self._seen = 0.0

    def update(self, total, size, name):
        self._seen  = size
        pct = (self._seen / total) * 100.0
        print '%s progress: %.2f' % (name, pct)

class file_with_callback(file):
    def __init__(self, path, mode, callback, *args):
        file.__init__(self, path, mode)
        self.seek(0, os.SEEK_END)
        self._total = self.tell()
        self.seek(0)
        self._callback = callback
        self._args = args

    def __len__(self):
        return self._total

    def read(self, size):
        data = file.read(self, size)
        self._callback(self._total, len(data), *self._args)
        return data

path = 'large_file.txt'
progress = Progress()
stream = file_with_callback(path, 'rb', progress.update, path)
req = urllib2.Request(url, stream)
res = urllib2.urlopen(req)
  

Вывод:

 large_file.txt progress: 0.68
large_file.txt progress: 1.36
large_file.txt progress: 2.04
large_file.txt progress: 2.72
large_file.txt progress: 3.40
...
large_file.txt progress: 99.20
large_file.txt progress: 99.87
large_file.txt progress: 100.00
  

Комментарии:

1. Почему вы поставили метод len ? Я не вижу, где httplib или вы используете его, какова цель?

2.Он используется в urllib2 AbstractHTTPHandler.do_request_() и в httplib HttpConnect._send_request() , где len(<file obj>) вызывается для установки Content-length заголовка.

3. Как вы могли бы использовать эту функцию с помощью StringIO?

Ответ №2:

запросы 2.0.0 содержат потоковые загрузки. Это означает, что вы можете использовать генератор для получения крошечных фрагментов и печати прогресса между фрагментами.

Ответ №3:

Я не думаю, что это возможно, но в pycurl есть обратные вызовы выполнения загрузки, которые вы можете использовать.

Комментарии:

1. Я пытаюсь сохранить его в стандартной библиотеке. Это будет распространяться среди пользователей Windows, и я не хочу, чтобы им приходилось устанавливать дополнительные материалы.

2. Ну, если вы что-то придумали, поделитесь этим здесь 🙂

Ответ №4:

poster поддерживает это

 import json
import os
import sys
import urllib2

from poster.encode import multipart_encode
from poster.streaminghttp import register_openers

def _upload_progress(param, current, total):
    sys.stdout.write(
        "r{} - {:.0f}%                "
        .format(param.name,
                (float(current) / float(total)) * 100.0))
    sys.stdout.flush()

def upload(request_resource, large_file_path):
    register_openers()
    with open(large_file_path, 'r') as large_file:
        request_data, request_headers = multipart_encode(
            [('file', largs_file)],
            cb=_upload_progress)

        request_headers.update({
            'X-HockeyAppToken': 'we use this for hockeyapp upload'
        })

        upload_request = urllib2.Request(request_resource,
                                         request_data, 
                                         request_headers)
        upload_connection = urllib2.urlopen(upload_request)
        upload_response = json.load(upload_connection)
    print "Done"