使用Python请求POST二进制(视频)文件

时间:2014-04-11 17:03:26

标签: python python-requests

我有一些PHP代码,它将二进制文件上传到我没有shell访问权限的远程服务器。 PHP代码是:

function upload($uri, $filename) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $uri);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, array('file' => '@' . $filename));
curl_exec($ch);
curl_close($ch);
}

这会产生如下标题:

HTTP/1.1
Host: XXXXXXXXX
Accept: */*
Content-Length: 208045596
Expect: 100-continue
Content-Type: multipart/form-data; boundary=----------------------------360aaccde050

我正在尝试使用请求将其移植到python,我无法让服务器接受我的POST。我已尝试过使用requests.post的每种方式,但标题不会模仿上述内容。

这将成功地将二进制文件传输到服务器(可以通过观看wireshark来判断),但因为标头不是服务器所期望的,它会被拒绝。 response_code虽然是200。

files = {'bulk_test2.mov': ('bulk_test2.mov', open('bulk_test2.mov', 'rb'))}
response = requests.post(url, files=files)

请求代码产生标题:

HTTP/1.1
Host: XXXX
Content-Length: 160
Content-Type: multipart/form-data; boundary=250852d250b24399977f365f35c4e060
Accept-Encoding: gzip, deflate, compress
Accept: */*
User-Agent: python-requests/2.2.1 CPython/2.7.5 Darwin/13.1.0

--250852d250b24399977f365f35c4e060
Content-Disposition: form-data; name="bulk_test2.mov"; filename="bulk_test2.mov"


--250852d250b24399977f365f35c4e060--

关于如何发出请求的任何想法都与PHP代码生成的标头匹配?

1 个答案:

答案 0 :(得分:5)

有两个很大的区别:

  1. PHP代码发布了一个名为file的字段,您的Python代码会发布一个名为bulk_test2.mov的字段。

  2. 您的Python代码发布文件。 Content-Length标头是160个字节,恰好是多部分边界和Content-Disposition部分标头占用的空间量。 bulk_test2.mov文件确实为空,或者您尝试多次发布文件而不重新打开或重新打开文件对象。

  3. 要解决第一个问题,请使用'file'作为files词典中的键:

    files = {'file': open('bulk_test2.mov', 'rb')}
    response = requests.post(url, files=files)
    

    我使用只是打开文件对象作为值;在这种情况下,requests将直接从文件对象获取文件名。

    第二个问题是你可以解决的问题。重复发帖时,请确保您没有重复使用 files。重新打开,或使用files['file'].seek(0)将阅读位置倒回到开头。

    Expect: 100-continue标头是一个可选的客户端功能,要求服务器confirm that the body upload can go ahead; 是一个必需的标题,任何发布文件对象的失败都不会归因于requests使用此功能。如果您不使用此功能导致HTTP服务器出现异常,则会违反HTTP RFC并且您手上会遇到更大的问题。它肯定不会成为requests能为您解决的问题。

    如果您确实设法发布实际文件数据,Content-Length中的任何小变化都是由于(随机)边界在Python和PHP之间的长度不同。这是正常,而不是上传问题的原因,除非您的目标服务器非常破坏。再一次,不要试图用Python修复这种破坏。

    但是,我假设你忽视了一些更简单的事情。例如,服务器可能会将某些User-Agent标头列入黑名单。您可以使用Session object

    清除部分默认标头requests
    files = {'file': open('bulk_test2.mov', 'rb')}
    session = requests.Session()
    del session.headers['User-Agent']
    del session.headers['Accept-Encoding']
    response = session.post(url, files=files)
    

    看看是否有所作为。

    如果服务器由于无法处理HTTP persistent connections而无法处理您的请求,您可以尝试将会话用作上下文管理器,以确保关闭所有会话连接:

    files = {'file': open('bulk_test2.mov', 'rb')}
    with requests.Session() as session:
        response = session.post(url, files=files, stream=True)
    

    你可以添加:

    response.raw.close()
    

    好的措施。