繁体   English   中英

使用 Python urllib/urllib2 发出 http POST 请求以上传文件

[英]Make an http POST request to upload a file using Python urllib/urllib2

我想使用 Python 发出 POST 请求以将文件上传到 Web 服务(并获得响应)。 例如,我可以使用curl执行以下 POST 请求:

curl -F "file=@style.css" -F output=json http://jigsaw.w3.org/css-validator/validator

如何使用 python urllib/urllib2 发出相同的请求? 到目前为止,我得到的最接近的是以下内容:

with open("style.css", 'r') as f:
    content = f.read()
post_data = {"file": content, "output": "json"}
request = urllib2.Request("http://jigsaw.w3.org/css-validator/validator", \
                          data=urllib.urlencode(post_data))
response = urllib2.urlopen(request)

我从上面的代码中得到了一个 HTTP 错误 500。 但是既然我的curl命令成功了,那一定是我的python请求有问题吧?

我对这个话题很陌生,我的问题可能有非常简单的答案或错误。

我个人认为您应该考虑使用请求库来发布文件。

url = 'http://jigsaw.w3.org/css-validator/validator'
files = {'file': open('style.css')}
response = requests.post(url, files=files)

使用urllib2上传文件并非不可能,而是一项相当复杂的任务: http : //pymotw.com/2/urllib2/#uploading-files

经过一番挖掘,这篇文章似乎解决了我的问题。 事实证明,我需要正确设置多部分编码器。

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

register_openers()

with open("style.css", 'r') as f:
    datagen, headers = multipart_encode({"file": f})
    request = urllib2.Request("http://jigsaw.w3.org/css-validator/validator", \
                              datagen, headers)
    response = urllib2.urlopen(request)

嗯,有多种方法可以做到。 如上所述,您可以在“multipart/form-data”中发送文件。 但是,目标服务可能不期望这种类型,在这种情况下,您可以尝试更多方法。

传递文件对象

urllib2 可以接受一个文件对象作为data 当您传递此类型时,库将文件作为二进制流读取并将其发送出去。 但是,它不会设置正确的Content-Type标头。 此外,如果缺少Content-Length标头,则它将尝试访问对象的len属性,该属性对于文件不存在。 也就是说,您必须同时提供Content-TypeContent-Length标头才能使该方法正常工作:

import os
import urllib2

filename = '/var/tmp/myfile.zip'
headers = {
    'Content-Type': 'application/zip',
    'Content-Length': os.stat(filename).st_size,
}
request = urllib2.Request('http://localhost', open(filename, 'rb'),
                          headers=headers)
response = urllib2.urlopen(request)

包装文件对象

为了不处理长度,您可以创建一个简单的包装对象。 如果您将文件加载到内存中,只需稍作更改,您就可以对其进行调整以从字符串中获取内容。

class BinaryFileObject:
  """Simple wrapper for a binary file for urllib2."""

  def __init__(self, filename):
    self.__size = int(os.stat(filename).st_size)
    self.__f = open(filename, 'rb')

  def read(self, blocksize):
    return self.__f.read(blocksize)

  def __len__(self):
    return self.__size

将内容编码为 base64

另一种方法是通过base64.b64encodedata进行base64.b64encode并提供Content-Transfer-Type: base64标头。 但是,这种方法需要服务器端的支持。 根据实现,服务可以接受文件并错误地存储它,或者返回HTTP 400 例如,GitHub API 不会抛出错误,但上传的文件将被损坏。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM