2010-03-23 20 views
13

Estoy cargando archivos potencialmente grandes en un servidor web. Actualmente estoy haciendo esto:Python: HTTP Publique un archivo grande con la transmisión

import urllib2 

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

Sin embargo, esto lee todo el contenido del archivo en la memoria antes de publicarlo. ¿Cómo puedo hacer que transmita el archivo al servidor?

+0

relacionadas: [archivo WSGI de streaming con un generador] (http : //stackoverflow.com/questions/11811404/) –

+0

Relacionados: http://stackoverflow.com/questions/2502596/python-http-post-alarge-file-with-streaming –

Respuesta

23

Al leer a través de la lista de correo tema enlazado por por systempuntoout, encontré una pista para la solución.

El módulo mmap le permite abrir un archivo que actúa como una cadena. Partes del archivo se cargan en la memoria a pedido.

Aquí está el código que estoy utilizando ahora:

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() 
+0

me alegro de que haya ayudado :) – systempuntoout

+0

no funciona Estoy recibiendo el error de la url – TinTin

+0

¿podría confirmar que la siguiente línea es correcta: request = urllib2.Request (url, mmapped_file_as_string) – TinTin

3

¿Has probado con Mechanize?

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

o, si no desea utilizar multipart/form-data, comprobar this antiguo puesto.

Se sugiere dos opciones:

1. Use mmap, Memory Mapped file object 
    2. Patch httplib.HTTPConnection.send 
+1

No estoy deseando enviar los archivos enc oded "multipart/form-data". Esto parece hacer eso. Solo estoy buscando una publicación en bruto. –

+0

En la opción # 2 de python se ha agregado parcheado ya, el tamaño del bloque es 8192, me pregunto por qué .. hmmm. ¿Cuál es la norma/estándar en esto? – MistahX

1

Try pycurl. No tengo nada configurado aceptará un archivo grande que no es en una POST multipart/form-data, pero aquí hay un ejemplo simple que lee el archivo según sea necesario.

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() 
+1

Gracias JimB. Lo hubiera usado, excepto que tengo algunas personas que usan Windows, y no quiero que tengan que instalar nada más. –

3

La documentación no dice que usted puede hacer esto, pero el código en urllib2 (y httplib) acepta cualquier objeto con un método de lectura() como datos. Entonces, usar un archivo abierto parece ser el truco.

Deberá configurar el encabezado Content-Length usted mismo. Si no está configurado, urllib2 llamará a len() en los datos, que los objetos de archivo no son compatibles.

import os.path 
import urllib2 

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

Este es el código relevante que maneja los datos que proporciona. Es de la clase HTTPConnection en httplib.py en 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) 
0

El uso de la biblioteca requests que puede hacer

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

como se mencionó here in their docs

+0

El tamaño del bloque 8K sigue vigente, ya que se llama a httplib.py, send() L # 869. –

Cuestiones relacionadas