Uploading Files and Multipart Form Data
π·οΈ APIs and HTTP Requests / Sending Data in Requests
When you need to send filesβlike logs, configuration backups, or imagesβto a server, standard JSON or form data won't work. Files require a special encoding format called multipart form data. This is how browsers upload files from HTML forms, and it's also how your Python scripts can send files to APIs.
βοΈ What Is Multipart Form Data?
Multipart form data is a way to send binary data (files) alongside regular text fields in a single HTTP request. Each piece of data is separated by a boundary marker, allowing the server to distinguish between different parts of the request.
Key characteristics: - Supports both text fields and file uploads in one request - Each part can have its own content type (e.g., image/png, text/csv) - The boundary string is automatically generated and included in the request headers - Required for any API endpoint that accepts file uploads
π οΈ How to Upload a Single File
To upload a file, you need to open it in binary mode and pass it as part of a dictionary to the files parameter. The server receives the file along with its filename and content type.
Basic file upload pattern: - Open the file using open('filename', 'rb') to read it in binary mode - Create a dictionary with the field name as the key and the file object as the value - Pass this dictionary to the files parameter of your request function - The library handles all the multipart encoding automatically
Example structure for a single file upload: - Import the requests library - Open the file in binary read mode - Store the file object in a dictionary: {'file': open('backup.log', 'rb')} - Send a POST request with files=file_dict - Close the file after the request completes
π Uploading Multiple Files
When you need to send several files at once, you can include multiple entries in the files dictionary. Each file gets its own field name, and the server processes them individually.
Multiple file upload pattern: - Open each file separately in binary mode - Create a dictionary with multiple keys, one per file - Example: {'log_file': open('error.log', 'rb'), 'config_file': open('settings.ini', 'rb')} - Send the request with the entire dictionary as the files parameter - The server receives each file under its respective field name
Alternative approach for lists of files: - Use a list of tuples instead of a dictionary - Each tuple contains: (field_name, (filename, file_object, content_type)) - This gives you more control over the filename sent to the server
π΅οΈ Combining Files with Form Data
Many upload endpoints require additional metadata alongside the fileβlike a description, user ID, or timestamp. You can send both files and regular form fields in the same request.
Combined data pattern: - Use the data parameter for text fields - Use the files parameter for file uploads - Both parameters can be passed to the same request - The library merges them into a single multipart request
Example fields you might send with a file: - description: A text field explaining what the file contains - category: A dropdown selection like "log" or "backup" - timestamp: The current date and time as a string
π§ͺ Comparison: JSON vs. Form Data vs. Multipart
| Feature | JSON | Form Data | Multipart Form Data |
|---|---|---|---|
| Data types supported | Text, numbers, booleans | Text only | Text + binary files |
| File upload support | No | No | Yes |
| Content-Type header | application/json | application/x-www-form-urlencoded | multipart/form-data |
| Human readable | Yes | Partially | No (binary encoded) |
| Use case | API data exchange | Simple form submissions | File uploads with metadata |
π§° Common File Upload Scenarios
Uploading a configuration file: - Open the config file in binary mode - Send it with a field name like 'config' - Include additional fields like 'environment' and 'version' in the data parameter
Uploading multiple log files: - Create a list of file paths to upload - Loop through the list and open each file - Build a dictionary with unique field names or use a list of tuples - Send all files in a single request
Uploading with custom filenames: - Use the tuple format: (field_name, (custom_name, file_object, mime_type)) - This lets you rename the file before sending it to the server - Useful when the local filename doesn't match what the server expects
β Best Practices for File Uploads
Always close your files: - Use the with statement to automatically close files after upload - Example: with open('file.txt', 'rb') as f: ensures cleanup
Set appropriate timeouts: - File uploads take longer than simple requests - Increase the timeout value to accommodate larger files - A timeout of 30-60 seconds is reasonable for most uploads
Handle large files carefully: - For very large files, consider streaming uploads - Monitor memory usage when opening multiple files simultaneously - Some servers have file size limitsβcheck the API documentation
Check the response status: - Always verify the server response after uploading - A 200 or 201 status means success - 400 or 413 errors often indicate file size or format issues
π Troubleshooting Common Issues
Server returns 400 Bad Request: - Verify the field name matches what the server expects - Check that the file format is supported - Ensure you're not mixing JSON and multipart incorrectly
File arrives corrupted or empty: - Confirm you opened the file in binary mode ('rb' not 'r') - Check that the file exists and has content before uploading - Verify the server isn't truncating the file
Upload hangs or times out: - Increase the request timeout value - Check your network connection speed - Consider compressing large files before upload
Content-Type mismatch: - Some servers require explicit MIME types - Pass the content type explicitly in the tuple format - Common types: 'text/plain', 'application/json', 'application/octet-stream'
File uploads are a fundamental skill when working with APIs that manage artifacts, logs, or configuration data. Once you understand the multipart format and how to structure your requests, you can integrate file handling into any automation or data pipeline with confidence.
This topic shows how to send files and form data together in a single HTTP request using Python's requests library.
π€ Example 1: Upload a single text file
This example uploads a simple text file to a server using the files parameter.
import requests
url = "https://httpbin.org/post"
file_content = open("example.txt", "rb")
files = {"file": file_content}
response = requests.post(url, files=files)
print(response.status_code)
π€ Output: 200
π€ Example 2: Upload a file with a custom filename
This example shows how to override the filename when sending a file to the server.
import requests
url = "https://httpbin.org/post"
file_content = open("photo.jpg", "rb")
files = {"image": ("profile_pic.jpg", file_content, "image/jpeg")}
response = requests.post(url, files=files)
print(response.json()["files"]["image"])
π€ Output: The raw file content as a string
π€ Example 3: Upload multiple files at once
This example sends two separate files in a single request using a dictionary.
import requests
url = "https://httpbin.org/post"
file1 = open("report.pdf", "rb")
file2 = open("summary.csv", "rb")
files = {
"document": file1,
"data": file2
}
response = requests.post(url, files=files)
print(response.status_code)
π€ Output: 200
π€ Example 4: Combine file upload with form data
This example sends both a file and regular form fields in one multipart request.
import requests
url = "https://httpbin.org/post"
file_content = open("invoice.pdf", "rb")
files = {"invoice": file_content}
form_data = {
"customer_id": "12345",
"date": "2025-03-20"
}
response = requests.post(url, files=files, data=form_data)
print(response.json()["form"]["customer_id"])
π€ Output: 12345
π€ Example 5: Upload a file from memory (string content)
This example sends a file created from a string without saving it to disk first.
import requests
url = "https://httpbin.org/post"
file_content = "Hello, this is file content created in memory."
files = {"upload": ("notes.txt", file_content, "text/plain")}
response = requests.post(url, files=files)
print(response.json()["files"]["upload"])
π€ Output: Hello, this is file content created in memory.
Comparison Table
| Feature | Example 1 | Example 2 | Example 3 | Example 4 | Example 5 |
|---|---|---|---|---|---|
| Single file upload | β | β | β | β | β |
| Custom filename | β | β | β | β | β |
| Multiple files | β | β | β | β | β |
| Form data included | β | β | β | β | β |
| File from memory | β | β | β | β | β |
When you need to send filesβlike logs, configuration backups, or imagesβto a server, standard JSON or form data won't work. Files require a special encoding format called multipart form data. This is how browsers upload files from HTML forms, and it's also how your Python scripts can send files to APIs.
βοΈ What Is Multipart Form Data?
Multipart form data is a way to send binary data (files) alongside regular text fields in a single HTTP request. Each piece of data is separated by a boundary marker, allowing the server to distinguish between different parts of the request.
Key characteristics: - Supports both text fields and file uploads in one request - Each part can have its own content type (e.g., image/png, text/csv) - The boundary string is automatically generated and included in the request headers - Required for any API endpoint that accepts file uploads
π οΈ How to Upload a Single File
To upload a file, you need to open it in binary mode and pass it as part of a dictionary to the files parameter. The server receives the file along with its filename and content type.
Basic file upload pattern: - Open the file using open('filename', 'rb') to read it in binary mode - Create a dictionary with the field name as the key and the file object as the value - Pass this dictionary to the files parameter of your request function - The library handles all the multipart encoding automatically
Example structure for a single file upload: - Import the requests library - Open the file in binary read mode - Store the file object in a dictionary: {'file': open('backup.log', 'rb')} - Send a POST request with files=file_dict - Close the file after the request completes
π Uploading Multiple Files
When you need to send several files at once, you can include multiple entries in the files dictionary. Each file gets its own field name, and the server processes them individually.
Multiple file upload pattern: - Open each file separately in binary mode - Create a dictionary with multiple keys, one per file - Example: {'log_file': open('error.log', 'rb'), 'config_file': open('settings.ini', 'rb')} - Send the request with the entire dictionary as the files parameter - The server receives each file under its respective field name
Alternative approach for lists of files: - Use a list of tuples instead of a dictionary - Each tuple contains: (field_name, (filename, file_object, content_type)) - This gives you more control over the filename sent to the server
π΅οΈ Combining Files with Form Data
Many upload endpoints require additional metadata alongside the fileβlike a description, user ID, or timestamp. You can send both files and regular form fields in the same request.
Combined data pattern: - Use the data parameter for text fields - Use the files parameter for file uploads - Both parameters can be passed to the same request - The library merges them into a single multipart request
Example fields you might send with a file: - description: A text field explaining what the file contains - category: A dropdown selection like "log" or "backup" - timestamp: The current date and time as a string
π§ͺ Comparison: JSON vs. Form Data vs. Multipart
| Feature | JSON | Form Data | Multipart Form Data |
|---|---|---|---|
| Data types supported | Text, numbers, booleans | Text only | Text + binary files |
| File upload support | No | No | Yes |
| Content-Type header | application/json | application/x-www-form-urlencoded | multipart/form-data |
| Human readable | Yes | Partially | No (binary encoded) |
| Use case | API data exchange | Simple form submissions | File uploads with metadata |
π§° Common File Upload Scenarios
Uploading a configuration file: - Open the config file in binary mode - Send it with a field name like 'config' - Include additional fields like 'environment' and 'version' in the data parameter
Uploading multiple log files: - Create a list of file paths to upload - Loop through the list and open each file - Build a dictionary with unique field names or use a list of tuples - Send all files in a single request
Uploading with custom filenames: - Use the tuple format: (field_name, (custom_name, file_object, mime_type)) - This lets you rename the file before sending it to the server - Useful when the local filename doesn't match what the server expects
β Best Practices for File Uploads
Always close your files: - Use the with statement to automatically close files after upload - Example: with open('file.txt', 'rb') as f: ensures cleanup
Set appropriate timeouts: - File uploads take longer than simple requests - Increase the timeout value to accommodate larger files - A timeout of 30-60 seconds is reasonable for most uploads
Handle large files carefully: - For very large files, consider streaming uploads - Monitor memory usage when opening multiple files simultaneously - Some servers have file size limitsβcheck the API documentation
Check the response status: - Always verify the server response after uploading - A 200 or 201 status means success - 400 or 413 errors often indicate file size or format issues
π Troubleshooting Common Issues
Server returns 400 Bad Request: - Verify the field name matches what the server expects - Check that the file format is supported - Ensure you're not mixing JSON and multipart incorrectly
File arrives corrupted or empty: - Confirm you opened the file in binary mode ('rb' not 'r') - Check that the file exists and has content before uploading - Verify the server isn't truncating the file
Upload hangs or times out: - Increase the request timeout value - Check your network connection speed - Consider compressing large files before upload
Content-Type mismatch: - Some servers require explicit MIME types - Pass the content type explicitly in the tuple format - Common types: 'text/plain', 'application/json', 'application/octet-stream'
File uploads are a fundamental skill when working with APIs that manage artifacts, logs, or configuration data. Once you understand the multipart format and how to structure your requests, you can integrate file handling into any automation or data pipeline with confidence.
Interactive Views
You are currently in π All-in-One mode. Use the tabs at the top to switch to π Theory Only or π» Code Only views.
This topic shows how to send files and form data together in a single HTTP request using Python's requests library.
π€ Example 1: Upload a single text file
This example uploads a simple text file to a server using the files parameter.
import requests
url = "https://httpbin.org/post"
file_content = open("example.txt", "rb")
files = {"file": file_content}
response = requests.post(url, files=files)
print(response.status_code)
π€ Output: 200
π€ Example 2: Upload a file with a custom filename
This example shows how to override the filename when sending a file to the server.
import requests
url = "https://httpbin.org/post"
file_content = open("photo.jpg", "rb")
files = {"image": ("profile_pic.jpg", file_content, "image/jpeg")}
response = requests.post(url, files=files)
print(response.json()["files"]["image"])
π€ Output: The raw file content as a string
π€ Example 3: Upload multiple files at once
This example sends two separate files in a single request using a dictionary.
import requests
url = "https://httpbin.org/post"
file1 = open("report.pdf", "rb")
file2 = open("summary.csv", "rb")
files = {
"document": file1,
"data": file2
}
response = requests.post(url, files=files)
print(response.status_code)
π€ Output: 200
π€ Example 4: Combine file upload with form data
This example sends both a file and regular form fields in one multipart request.
import requests
url = "https://httpbin.org/post"
file_content = open("invoice.pdf", "rb")
files = {"invoice": file_content}
form_data = {
"customer_id": "12345",
"date": "2025-03-20"
}
response = requests.post(url, files=files, data=form_data)
print(response.json()["form"]["customer_id"])
π€ Output: 12345
π€ Example 5: Upload a file from memory (string content)
This example sends a file created from a string without saving it to disk first.
import requests
url = "https://httpbin.org/post"
file_content = "Hello, this is file content created in memory."
files = {"upload": ("notes.txt", file_content, "text/plain")}
response = requests.post(url, files=files)
print(response.json()["files"]["upload"])
π€ Output: Hello, this is file content created in memory.
Comparison Table
| Feature | Example 1 | Example 2 | Example 3 | Example 4 | Example 5 |
|---|---|---|---|---|---|
| Single file upload | β | β | β | β | β |
| Custom filename | β | β | β | β | β |
| Multiple files | β | β | β | β | β |
| Form data included | β | β | β | β | β |
| File from memory | β | β | β | β | β |