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



我有一段PHP代码,可以将二进制文件上传到我没有shell访问权限的远程服务器

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. PHP代码发布了一个名为file的字段,Python代码发布了名为bulk_test2.mov的字段。

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

要解决第一个问题,请使用'file'作为files字典中的密钥:

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

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

第二个问题只有你自己才能解决。重复张贴时,请确保不要重复使用files。重新打开,或使用files['file'].seek(0)将读取位置倒回起始位置。

Expect: 100-continue报头是一个可选的客户端功能,它要求服务器确认可以进行主体上传;不是所需的标头,发布文件对象的任何失败都不会是因为requests是否使用了此功能。如果你不使用这个功能,HTTP服务器就会行为不端,这就违反了HTTP RFC,你会遇到更大的问题。这肯定不是requests可以为您解决的问题。

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

然而,我认为你忽略了一些简单得多的东西。例如,服务器可能会将某些User-Agent标头列入黑名单。您可以通过使用Session对象来清除一些默认标头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持久连接而无法处理您的请求,您可以尝试将会话用作上下文管理器,以确保所有会话连接都已关闭:

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

你可以添加:

response.raw.close()

在很大程度上。

最新更新