Basically, if you specify a files
parameter (a dictionary), then requests
will send a multipart/form-data
POST instead of a application/x-www-form-urlencoded
POST. You are not limited to using actual files in that dictionary, however:
>>>
import requests
>>>
response = requests.post('http://httpbin.org/post', files = dict(foo = 'bar')) >>>
response.status_code
200
and httpbin.org lets you know what headers you posted with; in response.json()
we have:
>>> from pprint
import pprint
>>>
pprint(response.json()['headers']) {
'Accept': '*/*',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Content-Length': '141',
'Content-Type': 'multipart/form-data; '
'boundary=c7cbfdd911b4e720f1dd8f479c50bc7f',
'Host': 'httpbin.org',
'User-Agent': 'python-requests/2.21.0'
}
I'd use the tuple form with None
as the filename, so that the filename="..."
parameter is dropped from the request for those parts:
>>> files = {
'foo': 'bar'
} >>>
print(requests.Request('POST', 'http://httpbin.org/post', files = files).prepare().body.decode('utf8'))
--bb3f05a247b43eede27a124ef8b968c5
Content - Disposition: form - data;
name = "foo";
filename = "foo"
bar
--bb3f05a247b43eede27a124ef8b968c5--
>>>
files = {
'foo': (None, 'bar')
} >>>
print(requests.Request('POST', 'http://httpbin.org/post', files = files).prepare().body.decode('utf8'))
--d5ca8c90a869c5ae31f70fa3ddb23c76
Content - Disposition: form - data;
name = "foo"
bar
--d5ca8c90a869c5ae31f70fa3ddb23c76--
There is also the excellent requests-toolbelt
project, which includes advanced Multipart support. It takes field definitions in the same format as the files
parameter, but unlike requests
, it defaults to not setting a filename parameter. In addition, it can stream the request from open file objects, where requests
will first construct the request body in memory:
from requests_toolbelt.multipart.encoder import MultipartEncoder mp_encoder = MultipartEncoder( fields = { 'foo': 'bar', # plain file object, no filename or mime type produces a # Content - Disposition header with just the part name 'spam': ('spam.txt', open('spam.txt', 'rb'), 'text/plain'), } ) r = requests.post( 'http://httpbin.org/post', data = mp_encoder, # The MultipartEncoder is posted as data, don 't use files=...! # The MultipartEncoder provides the content - type header with the boundary: headers = { 'Content-Type': mp_encoder.content_type } )
In short, the files
parameter takes a dictionary with the key being the name of the form field and the value being either a string or a 2, 3 or 4-length tuple, as described in the section POST a Multipart-Encoded File in the Requests quickstart:
>>> url = 'http://httpbin.org/post' >>>
files = {
'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {
'Expires': '0'
})
}
In the above, the tuple is composed as follows:
(filename, data, content_type, headers)
If the value is just a string, the filename will be the same as the key, as in the following:
>>> files = {
'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'
}
Content - Disposition: form - data;
name = "obvius_session_id";
filename = "obvius_session_id"
Content - Type: application / octet - stream
72 c2b6f406cdabd578c5fd7598557c52
From the original requests source:
def request(method, url, **kwargs):
"""Constructs and sends a :class:`Request <Request>`.
...
:param files: (optional) Dictionary of ``'name': file-like-objects``
(or ``{'name': file-tuple}``) for multipart encoding upload.
``file-tuple`` can be a 2-tuple ``('filename', fileobj)``,
3-tuple ``('filename', fileobj, 'content_type')``
or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``,
where ``'content-type'`` is a string
defining the content type of the given file
and ``custom_headers`` a dict-like object
containing additional headers to add for the file.
Based on the above, the simplest multipart form request that includes both files to upload and form fields will look like this:
import requests
multipart_form_data = {
'upload': ('custom_file_name.zip', open('myfile.zip', 'rb')),
'action': (None, 'store'),
'path': (None, '/path1')
}
response = requests.post('https://httpbin.org/post', files = multipart_form_data)
print(response.content)
If you need to post multiple fields with the same name then instead of a dictionary you can define your payload as a list (or a tuple) of tuples:
multipart_form_data = (
('file2', ('custom_file_name.zip', open('myfile.zip', 'rb'))),
('action', (None, 'store')),
('path', (None, '/path1')),
('path', (None, '/path2')),
('path', (None, '/path3')),
)
If you need to send multiple fields with the same name, or if the order of form fields is important, then a tuple or a list can be used instead of a dictionary:
multipart_data = MultipartEncoder(
fields = (
('action', 'ingest'),
('item', 'spam'),
('item', 'sausage'),
('item', 'eggs'),
)
)
Here is the simple code snippet to upload a single file with additional parameters using requests:
url = 'https://<file_upload_url>'
fp = '/Users/jainik/Desktop/data.csv'
files = {'file': open(fp, 'rb')}
payload = {'file_id': '1234'}
response = requests.put(url, files=files, data=payload, verify=False)
You need to use the name
attribute of the upload file that is in the HTML of the site. Example:
autocomplete = "off"
name = "image" >
script:
import requests site = 'https://prnt.sc/upload.php' # the site where you upload the file filename = 'image.jpg' # name example
Here, in the place of image, add the name of the upload file in HTML
up = {
'image': (filename, open(filename, 'rb'), "multipart/form-data")
}
Then start the request
request = requests.post(site, files = up, data = data)
You can print out the Content-Type
header of the request to verify the above using the example given below, which shows how to upload multiple files (or a single file) with (optionally) the same key
(i.e., 'files'
in the case below), as well as with optional form
data (i.e., data=form_data
). The documentation on how to POST
single and multiple files
can be found here and here, respectively. In case you need to upload large files without reading them into memory, have a look at Streaming Uploads.
For the server side - in case you need one - please have a look at this answer, from which the code snippet below has been taken, and which uses FastAPI web framework.
import requests url = 'http://127.0.0.1:8000/submit' files = [('files', open('test_files/a.txt', 'rb')), ('files', open('test_files/b.txt', 'rb'))] #file = { 'file': open('test_files/a.txt', 'rb') } # for sending a single file form_data = { "name": "foo", "point": 0.13, "is_accepted": False } resp = requests.post(url = url, data = form_data, files = files) print(resp.json()) print(resp.request.headers['content-type'])
In this tutorial we'll demonstrate how to upload a file from a Python server to another server by sending a POST request with multipart/form-data using the Python requests library.,The handle_form() of our flask application receives the posted file but doesn't save it. Instead, we'll use the requests library to upload it to the django server.,Now, let's proceed to create the uploading Python server that will make use of the Requests library to send a POST requests to the 127.0.0.1:8000/upload endpoint for uploading a file between two servers. ,Note: Typically we upload files from a client to a server but in this tutorial, we'll see how we can upload files from a server to another web server using Python and the Requests library.
$ python3 - m venv.env $ source.env / bin / activate
$ git clone https: //github.com/techiediaries/django-rest-file-upload.git server2
$ cd server2
$ pip install - r requirments.txt
$ python manage.py makemigrations $ python manage.py migrate $ python manage.py runserver
$ pip install requests
$ pip install flask
import os
from flask
import Flask, request, render_template
import requests
app = Flask(__name__)
@app.route('/handle_form', methods = ['POST'])
def handle_form():
print("Posted file: {}".format(request.files['file']))
file = request.files['file']
return ""
@app.route("/")
def index():
return render_template("index.html");
if __name__ == "__main__":
app.run(host = '0.0.0.0', port = 8080, debug = True)
In this chapter, we will upload a file using request and read the contents of the file uploaded. We can do it using the files param as shown in the example below.,Requests - Web Scraping using Requests,It is also possible to send the contents of the file as shown below−,We make use of cookies to improve our user experience. By using this website, you agree with our Cookies Policy. Agree Learn more
Example
import requests
myurl = 'https://httpbin.org/post'
files = {
'file': open('test.txt', 'rb')
}
getdata = requests.post(myurl, files = files)
print(getdata.text)
Test.txt
File upload test using Requests
Output
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 = 28 aee3a9d15a3571fb80d4d2a94bfd33 ", "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" }