Any suggestions?

Use Streaming Upload, as docs put it:

Requests supports streaming uploads, which allow you to send large streams or files without reading them into memory. To stream and upload, simply provide a file-like object for your body:

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)
Answer from Daweo on Stack Overflow
Top answer
1 of 2
4

Any suggestions?

Use Streaming Upload, as docs put it:

Requests supports streaming uploads, which allow you to send large streams or files without reading them into memory. To stream and upload, simply provide a file-like object for your body:

with open('massive-body', 'rb') as f:
    requests.post('http://some.url/streamed', data=f)
2 of 2
1

When you pass files arg then requests lib makes a multipart form upload. i.e. it is like submitting a form, where the file is passed as a named field (file in your example)

I suspect the problem you saw is because when you pass a file object as data arg, as suggested in the docs here https://requests.readthedocs.io/en/latest/user/advanced/#streaming-uploads then it does a streaming upload but the file content is used as the whole http post body.

So I think the server at the other end is expecting a form with a file field, but we're just sending the binary content of the file by itself.

What we need is some way to wrap the content of the file with the right "envelope" as we send it to the server, so that it can recognise the data we are sending.

See this issue where others have noted the same problem: https://github.com/psf/requests/issues/1584

I think the best suggestion from there is to use this additional lib, which provides streaming multipart form file upload: https://github.com/requests/toolbelt#multipartform-data-encoder

For example:

from requests_toolbelt import MultipartEncoder
import requests

encoder = MultipartEncoder(
    fields={'file': ('myfilename.xyz', open(path, 'rb'), 'text/plain')}
)
response = requests.post(
    url, data=encoder, headers={'Content-Type': encoder.content_type}
)
Discussions

Can't upload file larger than 2GB to webdav by requests
Dear Sir, I can't upload file larger than 2GB to webdav server by requests, and the following is the error message. Any idea about how to solve this problem ? Thanks a lot for your help. OS: SL5,SL6 Python version:2.6.6 requests version:2.1.0 More on github.com
🌐 github.com
25
December 6, 2013
Python: HTTP Post a large file with streaming - Stack Overflow
The requests module is great, but sometimes you cannot install any extra modules... ... Find the answer to your question by asking. Ask question ... See similar questions with these tags. ... 3 python pip install poster gives me error --> Command "python setup.py egg_info" failed with error code 1 in C:\Users\ 2 How to send huge files over HTTPPost method in Python, upload large ... More on stackoverflow.com
🌐 stackoverflow.com
python requests: post and big content - Stack Overflow
0 How to upload large files with POST without loading it in the RAM? 0 OverflowError: string longer than 2147483647 bytes · 2 Python requests sending Content-Length 0 in POST request More on stackoverflow.com
🌐 stackoverflow.com
April 7, 2014
Uploading of large files/archives through Python API results in ConnectionAbortedError [WinError 10054]
Uploading of large files/archives through Python API results in ConnectionAbortedError [WinError 10054]#2328 ... Uploads using request.put() are randomly and abruptly stopped anywhere between being 10 and 99% uploaded. More on github.com
🌐 github.com
10
May 18, 2022
🌐
FastAPI
fastapi.tiangolo.com › tutorial › request-files
Request Files - FastAPI
To receive uploaded files, first install python-multipart. Make sure you create a virtual environment, activate it, and then install it, for example: ... This is because uploaded files are sent as "form data". ... from typing import Annotated from fastapi import FastAPI, File, UploadFile app = FastAPI() @app.post("/files/") async def create_file(file: Annotated[bytes, File()]): return {"file_size": len(file)} @app.post("/uploadfile/") async def create_upload_file(file: UploadFile): return {"filename": file.filename}
🌐
api.video
api.video › blog › tutorials › upload-a-big-video-file-using-python
Upload a big video file using Python
This sample works by opening, reading, and sending your video file as chunks of binary data. When it arrives, it's encoded into HLS for you, no matter what kind of video it started out as. ## How to upload a large video that is over 199 MiB to api.video. (Though this script will also work for videos under 200 MiB if you want to test it out.) import requests import os ## Set up variables for endpoints (we will create the third URL programmatically later) auth_url = "https://ws.api.video/auth/api-key" create_url = "https://ws.api.video/videos" ## Set up headers and payload for first authentication request headers = { "Accept": "application/json", "Content-Type": "application/json" } payload = { "apiKey": "your API key here" } ## Send the first authentication request to get a token.
🌐
GitHub
github.com › psf › requests › issues › 1784
Can't upload file larger than 2GB to webdav by requests · Issue #1784 · psf/requests
December 6, 2013 - raceback (most recent call last): File "upload.py", line 5, in File "/opt/rucio/.venv/lib64/python2.6/site-packages/requests/api.py", line 99, in put return request('put', url, data=data, *_kwargs) File "/opt/rucio/.venv/lib64/python2.6/site-packages/requests/api.py", line 44, in request return session.request(method=method, url=url, *_kwargs) File "/opt/rucio/.venv/lib64/python2.6/site-packages/requests/sessions.py", line 361, in request resp = self.send(prep, *_send_kwargs) File "/opt/rucio/.venv/lib64/python2.6/site-packages/requests/sessions.py", line 464, in send r = adapter.send(request, *_kwargs) File "/opt/rucio/.venv/lib64/python2.6/site-packages/requests/adapters.py", line 363, in send raise SSLError(e) requests.exceptions.SSLError: [Errno 5] _ssl.c:1217: Some I/O error occurred
Author   wchang
Top answer
1 of 6
31

Reading through the mailing list thread linked to by systempuntoout, I found a clue towards the solution.

The mmap module allows you to open file that acts like a string. Parts of the file are loaded into memory on demand.

Here's the code I'm using now:

Copyimport urllib2
import mmap

# Open the file as a memory mapped string. Looks like a string, but 
# actually accesses the file behind the scenes. 
f = open('somelargefile.zip','rb')
mmapped_file_as_string = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)

# Do the request
request = urllib2.Request(url, mmapped_file_as_string)
request.add_header("Content-Type", "application/zip")
response = urllib2.urlopen(request)

#close everything
mmapped_file_as_string.close()
f.close()
2 of 6
5

The documentation doesn't say you can do this, but the code in urllib2 (and httplib) accepts any object with a read() method as data. So using an open file seems to do the trick.

You'll need to set the Content-Length header yourself. If it's not set, urllib2 will call len() on the data, which file objects don't support.

Copyimport os.path
import urllib2

data = open(filename, 'r')
headers = { 'Content-Length' : os.path.getsize(filename) }
response = urllib2.urlopen(url, data, headers)

This is the relevant code that handles the data you supply. It's from the HTTPConnection class in httplib.py in Python 2.7:

Copydef send(self, data):
    """Send `data' to the server."""
    if self.sock is None:
        if self.auto_open:
            self.connect()
        else:
            raise NotConnected()

    if self.debuglevel > 0:
        print "send:", repr(data)
    blocksize = 8192
    if hasattr(data,'read') and not isinstance(data, array):
        if self.debuglevel > 0: print "sendIng a read()able"
        datablock = data.read(blocksize)
        while datablock:
            self.sock.sendall(datablock)
            datablock = data.read(blocksize)
    else:
        self.sock.sendall(data)
🌐
TutorialsPoint
tutorialspoint.com › requests › requests_file_upload.htm
File Upload with Requests in Python
import requests myurl = 'https://httpbin.org/post' files = {'file': open('test.txt', 'rb')} getdata = requests.post(myurl, files=files) print(getdata.text) ... E:\prequests>python makeRequest.py { "args": {}, "data": "", "files": { "file": "File upload test using Requests" }, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Content-Length": "175", "Content-Type": "multipart/form-data; boundary=28aee3a9d15a3571fb80d4d2a94bfd33", "Host": "httpbin.org", "User-Agent": "python-requests/2.22.0" }, "json": null, "origin": "117.223.63.135, 117.223.63.135", "url": "https://httpbin.org/post" }
Find elsewhere
🌐
GitHub
github.com › zenodo › zenodo › issues › 2328
Uploading of large files/archives through Python API results in ConnectionAbortedError [WinError 10054] · Issue #2328 · zenodo/zenodo
May 18, 2022 - Default chunk size: 1k.""" while True: data = file_object.read(blocksize) if not data: break yield data with open(filepath, "rb") as fp: r = s.put(f"{url}/{filepath.name}", data = read_in_chunks(fp, filepath.stat().st_size), params = {"access_token":xxx}) Interestingly - Chunk Encoding sometimes works to reduce the ConnectionReset frequency for larger files, which then manage to more frequently fully complete.
Published   May 18, 2022
Author   PeterBetlem
🌐
Sonatype Community
community.sonatype.com › sonatype nexus repository
Can you upload large tarballs using Python3 requests by chunking the file? - Sonatype Nexus Repository - Sonatype Community
August 4, 2022 - I have been able to successfully upload some of the tarballs using python3 requests.post, but some of the tarballs are too big and I get a MemoryError from python. Through some research, I found the best solution for my situation would be to chunk the tarballs into smaller, multipart uploads.
🌐
py4u
py4u.org › blog › python-requests-upload-large-file-with-additional-data
How to Upload Large Files with Additional Data Using Python Requests Without MemoryError: A Complete Guide
Python 3.6+: Download from python.org. ... Let’s first demonstrate the bad practice that causes MemoryError. Suppose we want to upload a 2GB file named large_file.dat to a server. import requests # ❌ This reads the ENTIRE file into memory! with open("large_file.dat", "rb") as f: file_content = f.read() # Loads 2GB into RAM url = "http://localhost:5000/upload" response = requests.post( url, files={"file": ("large_file.dat", file_content, "application/octet-stream")} ) print(response.status_code)
Top answer
1 of 9
1019

The missing piece is to turn on streaming with stream=True—that's what tells Requests not to read the whole content into memory before you have a chance to look at it.

With the following streaming code, the Python memory usage is restricted regardless of the size of the downloaded file:

def download_file(url):
    local_filename = url.split('/')[-1]
    # NOTE the stream=True parameter below
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        with open(local_filename, 'wb') as f:
            for chunk in r.iter_content(chunk_size=8192): 
                # If you have chunk encoded response uncomment if
                # and set chunk_size parameter to None.
                #if chunk: 
                f.write(chunk)
    return local_filename

Note that the number of bytes returned using iter_content is not exactly the chunk_size; it's expected to be a random number that is often far bigger, and is expected to be different in every iteration.

See body-content-workflow and Response.iter_content for further reference.

2 of 9
568

It's much easier if you use Response.raw and shutil.copyfileobj():

import requests
import shutil

def download_file(url):
    local_filename = url.split('/')[-1]
    with requests.get(url, stream=True) as r:
        with open(local_filename, 'wb') as f:
            shutil.copyfileobj(r.raw, f)

    return local_filename

This streams the file to disk without using excessive memory, and the code is simple.

Note: According to the documentation, Response.raw will not decode gzip and deflate transfer-encodings, so you will need to do this manually.

🌐
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 - with open('large_video.mp4', 'rb') as f: requests.post('https://example.com/upload', files={'video': f}) Instead, we can create a file-like object that streams the data in chunks: ... This streams the file data in memory-efficient chunks without ...
🌐
cyberangles
cyberangles.org › blog › using-python-requests-to-bridge-a-file-without-loading-into-memory
How to Stream Large Files from URL to Multipart POST with Python Requests (Without Loading into Memory) — CyberAngles.org
This blog post will guide you through a solution to this problem: streaming a large file directly from a URL to a multipart/form-data POST request using Python’s requests library—without ever loading the entire file into memory.
🌐
Pythonrequests
pythonrequests.com › python-requests-post-large-file
python requests post large file
July 12, 2023 - import requests url = ... the content type to multipart/form-data. You can also use chunked encoding to upload large files using Python requests library....
🌐
GitHub
github.com › psf › requests › issues › 2181
Requests is slow to upload large files · Issue #2181 · psf/requests
August 26, 2014 - I am using requests to POST data to a server. In testing with a 14.4 MB file, requests took 16.5 seconds and cURL took 3.3 seconds. The following image shows the 3 tests I ran. The first bump is using requests with MultipartEncoder from requests-toolbelt. The second bump is just using requests. The last bump is cURL. I am using Python 3.4.0, requests 2.3.0, and requests-toolbelt 0.3.1 on Windows 7 x64.
Author   Tech356
🌐
kmiku7's blog
kmiku7.github.io › 2018 › 11 › 16 › Why-uploading-large-file-using-Requests-is-very-slow
Why uploading large file using Requests is very slow? | kmiku7's blog
September 20, 2019 - Recently I write a python script to upload files using Requests Libaray. The script need about 7 miniutes to upload a file about 3GB in size. While the curl only take less than a miniute.According ser
🌐
GitHub
gist.github.com › nbari › 7335384
python chunk upload files · GitHub
python chunk upload files. GitHub Gist: instantly share code, notes, and snippets.