Python POST binary data Python POST binary data python python

Python POST binary data


Basically what you do is correct. Looking at redmine docs you linked to, it seems that suffix after the dot in the url denotes type of posted data (.json for JSON, .xml for XML), which agrees with the response you get - Processing by AttachmentsController#upload as XML. I guess maybe there's a bug in docs and to post binary data you should try using http://redmine/uploads url instead of http://redmine/uploads.xml.

Btw, I highly recommend very good and very popular Requests library for http in Python. It's much better than what's in the standard lib (urllib2). It supports authentication as well but I skipped it for brevity here.

import requestswith open('./x.png', 'rb') as f:    data = f.read()res = requests.post(url='http://httpbin.org/post',                    data=data,                    headers={'Content-Type': 'application/octet-stream'})# let's check if what we sent is what we intended to send...import jsonimport base64assert base64.b64decode(res.json()['data'][len('data:application/octet-stream;base64,'):]) == data

UPDATE

To find out why this works with Requests but not with urllib2 we have to examine the difference in what's being sent. To see this I'm sending traffic to http proxy (Fiddler) running on port 8888:

Using Requests

import requestsdata = 'test data'res = requests.post(url='http://localhost:8888',                    data=data,                    headers={'Content-Type': 'application/octet-stream'})

we see

POST http://localhost:8888/ HTTP/1.1Host: localhost:8888Content-Length: 9Content-Type: application/octet-streamAccept-Encoding: gzip, deflate, compressAccept: */*User-Agent: python-requests/1.0.4 CPython/2.7.3 Windows/Vistatest data

and using urllib2

import urllib2data = 'test data'    req = urllib2.Request('http://localhost:8888', data)req.add_header('Content-Length', '%d' % len(data))req.add_header('Content-Type', 'application/octet-stream')res = urllib2.urlopen(req)

we get

POST http://localhost:8888/ HTTP/1.1Accept-Encoding: identityContent-Length: 9Host: localhost:8888Content-Type: application/octet-streamConnection: closeUser-Agent: Python-urllib/2.7test data

I don't see any differences which would warrant different behavior you observe. Having said that it's not uncommon for http servers to inspect User-Agent header and vary behavior based on its value. Try to change headers sent by Requests one by one making them the same as those being sent by urllib2 and see when it stops working.


This has nothing to do with a malformed upload. The HTTP error clearly specifies 401 unauthorized, and tells you the CSRF token is invalid. Try sending a valid CSRF token with the upload.

More about csrf tokens here:

What is a CSRF token ? What is its importance and how does it work?


you need to add Content-Disposition header, smth like this (although I used mod-python here, but principle should be the same):

request.headers_out['Content-Disposition'] = 'attachment; filename=%s' % myfname