Python: HTTP Опубликовать большой файл с потоковой передачей
Я загружаю потенциально большие файлы на веб-сервер. В настоящее время я делаю это:
import urllib2
f = open('somelargefile.zip','rb')
request = urllib2.Request(url,f.read())
request.add_header("Content-Type", "application/zip")
response = urllib2.urlopen(request)
Однако, прежде чем отправлять его, он считывает содержимое всего файла в памяти. Как я могу передать файл на сервер?
Ответы
Ответ 1
Чтение через поток списка рассылки, связанный с systempuntoout, я нашел ключ к решению.
Модуль 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()
Ответ 2
Вы пробовали с Mechanize?
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
Ответ 3
В документации не говорится, что вы можете это сделать, но код в 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)
Ответ 4
Попробуйте 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()
Ответ 5
Используя библиотеку requests
, вы можете сделать
with open('massive-body', 'rb') as f:
requests.post('http://some.url/streamed', data=f)
как упоминалось здесь, в своих документах