Python: HTTP Опубликовать большой файл с потоковой передачей - PullRequest
16 голосов
/ 23 марта 2010

Я загружаю потенциально большие файлы на веб-сервер. В настоящее время я делаю это:

import urllib2

f = open('somelargefile.zip','rb')
request = urllib2.Request(url,f.read())
request.add_header("Content-Type", "application/zip")
response = urllib2.urlopen(request)

Тем не менее, это считывает все содержимое файла в память перед отправкой. Как сделать так, чтобы файл транслировался на сервер?

Ответы [ 6 ]

28 голосов
/ 24 марта 2010

Читая ветку списка рассылки, связанную с systemptotoout, я нашел ключ к решению.

Модуль mmap позволяет открывать файл, который действует как строка. Части файла загружаются в память по требованию.

Вот код, который я сейчас использую:

import urllib2
import mmap

# Open the file as a memory mapped string. Looks like a string, but 
# actually accesses the file behind the scenes. 
f = open('somelargefile.zip','rb')
mmapped_file_as_string = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)

# Do the request
request = urllib2.Request(url, mmapped_file_as_string)
request.add_header("Content-Type", "application/zip")
response = urllib2.urlopen(request)

#close everything
mmapped_file_as_string.close()
f.close()
3 голосов
/ 12 июня 2015

В документации не сказано, что вы можете сделать это, но код в urllib2 (и httplib) принимает любой объект с методом read () в качестве данных. Таким образом, использование открытого файла, кажется, делает свое дело.

Вам нужно установить заголовок Content-Length самостоятельно. Если он не установлен, urllib2 вызовет len () для данных, которые файловые объекты не поддерживают.

import os.path
import urllib2

data = open(filename, 'r')
headers = { 'Content-Length' : os.path.getsize(filename) }
response = urllib2.urlopen(url, data, headers)

Это соответствующий код, который обрабатывает предоставленные вами данные. Это из HTTPConnection класса в httplib.py в Python 2.7:

def send(self, data):
    """Send `data' to the server."""
    if self.sock is None:
        if self.auto_open:
            self.connect()
        else:
            raise NotConnected()

    if self.debuglevel > 0:
        print "send:", repr(data)
    blocksize = 8192
    if hasattr(data,'read') and not isinstance(data, array):
        if self.debuglevel > 0: print "sendIng a read()able"
        datablock = data.read(blocksize)
        while datablock:
            self.sock.sendall(datablock)
            datablock = data.read(blocksize)
    else:
        self.sock.sendall(data)
3 голосов
/ 23 марта 2010

Вы пробовали с Механизировать ?

from mechanize import Browser
br = Browser()
br.open(url)
br.form.add_file(open('largefile.zip'), 'application/zip', 'largefile.zip')
br.submit()

или, если вы не хотите использовать multipart / form-data, отметьте это старое сообщение.

Предлагает два варианта:

  1. Use mmap, Memory Mapped file object
  2. Patch httplib.HTTPConnection.send
1 голос
/ 30 июня 2016

Используя библиотеку requests, вы можете сделать

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)

как уже упоминалось здесь, в своих документах

1 голос
/ 23 марта 2010

Попробуйте Pycurl. У меня нет ничего, что программа установки примет большой файл, который не в POST multipart / form-data, но вот простой пример, который читает файл по мере необходимости.

import os
import pycurl

class FileReader:
    def __init__(self, fp):
        self.fp = fp
    def read_callback(self, size):
        return self.fp.read(size)

c = pycurl.Curl()
c.setopt(pycurl.URL, url)
c.setopt(pycurl.UPLOAD, 1)
c.setopt(pycurl.READFUNCTION, FileReader(open(filename, 'rb')).read_callback)
filesize = os.path.getsize(filename)
c.setopt(pycurl.INFILESIZE, filesize)
c.perform()
c.close()
0 голосов
/ 20 августа 2018

Не может (пока) комментировать ответ Брайана https://stackoverflow.com/a/30810626/9921853

urllib2.urlopen (url, data, headers) не принимает заголовки в качестве параметра, поэтому он не будет работать

Ниже приведен рабочий пример для Python 2 / Python 3:

try:
    from urllib2 import urlopen, Request
except:
    from urllib.request import urlopen, Request

headers = { 'Content-length': str(os.path.getsize(filepath)) }
with open(filepath, 'rb') as f:
    req = Request(url, data=f, headers=headers)
    result = urlopen(req).read().decode()

Модуль запросов великолепен, но иногда вы не можете установить дополнительные модули ...

...