it may because you use multipart/form to upload file.try use data like code below

data = open(localFilePath, 'rb').read()
headers = {
    "Content-Type":"application/binary",
}
upload = requests.put(uploadUrl,data=data,headers=headers)
Answer from shutup on Stack Overflow
🌐
GitHub
gist.github.com › yoavram › 4351498
Example of uploading binary files programmatically in python, including both client and server code. Client implemented with the requests library and the server is implemented with the flask library. · GitHub
Example of uploading binary files programmatically in python, including both client and server code. Client implemented with the requests library and the server is implemented with the flask libra...
🌐
GitHub
github.com › psf › requests › issues › 1266
How to upload binary file using PUT? · Issue #1266 · psf/requests
March 26, 2013 - def filegen(fo): yield fo.read(CHUNK_SIZE) def main(): session = requests.Session() session.auth = HTTPKerberosAuth() upload_headers = {'X-Codesigner-security-token': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'} with open(pathname, 'rb') as infile: response = session.put(upload_url, headers=upload_headers, data=filegen(infile)) ... Request headers: { 'Accept-Encoding': 'gzip, deflate, compress', 'X-Codesigner-security-token': 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', 'Accept': '*/*', 'User-Agent': 'python-requests/1.1.0 CPython/2.7.1 Windows/7', 'Transfer-Encoding': 'chunked', 'Authorization': 'Negotiate <bl
Author   dabono
Top answer
1 of 4
104

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 requests
with 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 json
import base64

assert 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 requests

data = '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.1
Host: localhost:8888
Content-Length: 9
Content-Type: application/octet-stream
Accept-Encoding: gzip, deflate, compress
Accept: */*
User-Agent: python-requests/1.0.4 CPython/2.7.3 Windows/Vista

test data

and using urllib2

import urllib2

data = '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.1
Accept-Encoding: identity
Content-Length: 9
Host: localhost:8888
Content-Type: application/octet-stream
Connection: close
User-Agent: Python-urllib/2.7

test 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.

2 of 4
4

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?

🌐
Pythonrequests
pythonrequests.com › python-requests-upload-binary-file
python requests upload binary file
July 12, 2023 - import requests url = 'http://example.com/upload' file_path = '/path/to/binary/file' with open(file_path, 'rb') as f: r = requests.post(url, files={'file': f})
🌐
Stack Abuse
stackabuse.com › how-to-upload-files-with-pythons-requests-library
How to Upload Files with Python's requests Library
September 19, 2021 - If you're uploading a file in the same directory, you can just use the file's name. The second argument, mode, will take the "read binary" value which is represented by rb. This argument tells the computer that we want to open the file in the read mode, and we wish to consume the data of the ...
🌐
Strapi Community
forum.strapi.io › questions and answers
How do I upload binary files using Python? - Questions and Answers - Strapi Community Forum
January 15, 2022 - ▶ System Information on the Upload documentation here: Upload - Strapi Developer Docs The docs give a code snippet for uploading a binary file in JavaScript. How do I do it in Python? This is ...
🌐
Medium
medium.com › codex › chunked-uploads-with-binary-files-in-python-f0c48e373a91
Chunked Uploads with Binary Files in Python | by Erikka Innes | CodeX | Medium
March 3, 2021 - My goal today, is to go over some of the sticking points you might encounter when trying to upload a large file in chunks that isn’t text. If you have something besides a text file, the first sticking point you’ll encounter is accidentally treating your file like a text file. If you find a tutorial that’s accurate for a text file, it will often work for a binary file as long as you include a few tweaks that help Python recognize the difference in your file.
🌐
Python-requests
docs.python-requests.org › en › v1.2.3 › user › quickstart
https://docs.python-requests.org/en/v1.2.3/user/qu...
Requests makes it simple to upload Multipart-encoded files: >>> url = 'http://httpbin.org/post' >>> files = {'file': open('report.xls', 'rb')} >>> r = requests.post(url, files=files) >>> r.text { ... "files": { "file": "<censored...binary...data>" }, ...
Find elsewhere
🌐
YouTube
youtube.com › watch
python requests post binary data - YouTube
Instantly Download or Run the code at https://codegive.com in this tutorial, we'll explore how to use the requests library in python to send post requests w...
Published   February 22, 2024
🌐
FastAPI
fastapi.tiangolo.com › tutorial › request-files
Request Files - FastAPI
This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory. You can get metadata from the uploaded file. It has a file-like async interface. It exposes an actual Python SpooledTemporaryFile object that you can pass directly to ...
🌐
ActiveState
code.activestate.com › recipes › 576422-python-http-post-binary-file-upload-with-pycurl
Python HTTP POST binary file upload with pycurl « Python recipes « ActiveState Code
August 14, 2008 - I didn't liked/tried solution explained ... http://curl.haxx.se/libcurl/) because it is std. lib for php, and uploading the file is very simple (just add @<path-to-file> to post variable value)....
🌐
GeeksforGeeks
geeksforgeeks.org › python › how-to-upload-files-using-python-requests-library
How to Upload Files Using Python Requests Library - GeeksforGeeks
July 23, 2025 - Before diving into file upload examples, let's ensure you have the requests library installed. If not, you can install it using pip: ... In this example, below code uses the Python requests library to upload a file (file.txt) to the specified URL (https://httpbin.org/post) using a POST request with the files parameter, and then prints the response text.
🌐
ProxiesAPI
proxiesapi.com › articles › streaming-uploads-in-python-requests-using-file-like-objects
Streaming Uploads in Python Requests using File-Like Objects | ProxiesAPI
February 3, 2024 - For smaller files under 10-20MB, avoid complex file streams. ... So in summary, file-like streaming objects allow efficient upload of large binary data in Python Requests while avoiding high memory usage.
🌐
ProxiesAPI
proxiesapi.com › articles › uploading-files-in-python-requests-a-guide
Uploading Files in Python Requests: A Guide | ProxiesAPI
Uploading a file is just a matter of attaching the binary data as well as metadata to identify the file contents. Here's an example of posting an image to an upload endpoint: ... import requests url = 'https://api.example.com/upload' files = ...
🌐
Python
bugs.python.org › issue11898
Issue 11898: Sending binary data with a POST request in httplib can cause Unicode exceptions - Python tracker
This issue tracker has been migrated to GitHub, and is currently read-only. For more information, see the GitHub FAQs in the Python's Developer Guide · This issue has been migrated to GitHub: https://github.com/python/cpython/issues/56107
🌐
CSDN
devpress.csdn.net › python › 63044f14c67703293080ac25.html
Python POST binary data - DevPress官方社区
August 23, 2022 - import urllib2, os FilePath = "C:\somefolder\somefile.7z" FileData = open(FilePath, "rb") length = os.path.getsize(FilePath) password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() password_manager.add_password(None, 'http://redmine/', 'admin', 'admin') auth_handler = urllib2.HTTPBasicAuthHandler(password_manager) opener = urllib2.build_opener(auth_handler) urllib2.install_opener(opener) request = urllib2.Request( r'http://redmine/uploads.xml', FileData) request.add_header('Content-Length', '%d' % length) request.add_header('Content-Type', 'application/octet-stream') try: response = urllib2.urlopen( request) print response.read() except urllib2.HTTPError as e: error_message = e.read() print error_message
Top answer
1 of 1
6

There are two large differences:

  1. The PHP code posts a field named file, your Python code posts a field named bulk_test2.mov.

  2. Your Python code posts an empty file. There Content-Length header is 160 bytes, exactly the amount of space the multipart boundaries and Content-Disposition part header take up. Either the bulk_test2.mov file is indeed empty, or you tried to post the file multiple times without rewinding or reopening the file object.

To fix the first problem, use 'file' as the key in your files dictionary:

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

I used just the open file object as the value; requests will get the filename directly from the file object in that case.

The second issue is something only you can fix. Make sure you don't reuse files when repeatedly posting. Reopen, or use files['file'].seek(0) to rewind the read position back to the start.

The Expect: 100-continue header is an optional client feature that asks the server to confirm that the body upload can go ahead; it is not a required header and any failure to post your file object is not going to be due to requests using this feature or not. If an HTTP server were to misbehave if you don't use this feature, it is in violation of the HTTP RFCs and you'll have bigger problems on your hands. It certainly won't be something requests can fix for you.

If you do manage to post actual file data, any small variations in Content-Length are due to the (random) boundary being a different length between Python and PHP. This is normal, and not the cause of upload problems, unless your target server is extremely broken. Again, don't try to fix such brokenness with Python.

However, I'd assume you overlooked something much simpler. Perhaps the server blacklists certain User-Agent headers, for example. You could clear some of the default headers requests sets by using a Session object:

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)

and see if that makes a difference.

If the server fails to handle your request because it fails to handle HTTP persistent connections, you could try to use the session as a context manager to ensure that all session connections are closed:

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

and you could add:

response.raw.close()

for good measure.

🌐
Intelligent-d2
intelligent-d2.com › python › use-python-requests-library-post-multipart-encoded-file
Use the python Requests library to post Multipart-Encoded file | Intelligent-d2
October 20, 2017 - 1.) We are importing the requests library so we can make HTTP requests using get, post, put, delete etc… · 2.) On line 4 we are opening a file(test.txt) in binary mode, which is recommended by the Requests docs because opening it in text mode may cause an error because Requests attempts to provide a Content-Length header for you, which is in bytes.
🌐
Brainly
brainly.com › computers and technology › high school › how do you fetch a binary file with python requests?
[FREE] How do you fetch a binary file with Python requests? - brainly.com
November 19, 2023 - Learn more about Fetching a binary ... fetch a binary file in Python, use the requests library to make a GET request to the file's URL and write the response content to a local file in binary mode....