File Uploads
Shipwell's API endpoints that accept file uploads do so in a consistent manner with support for multipart/form-data uploads.
The API utilizes multipart form data for file uploads since this is type of file upload foundational to the internet, is the most common file upload format for APIs and the web, and it has wide support in all known web-accessible programming languages (i.e. cURL
, Python
, Java
, JavaScript
, PHP
, etc.).
Multipart Form Data File Uploads
As described in many examples elsewhere on the web, multipart requests combine one or more sets (and potentially types) of data into one request.
The different parts of the data are separated or delimited by a specified boundary marker (i.e. this can be anything, is set in the request, and is often handled in the background by programming languages and libraries without the developer or integrator needing to consider this).
We recommend using libraries to help with your programming and integration (as you are likely already doing) as they make tasks like file uploads simpler and essentially remove the potential errors from reinventing the wheel.
For example, in python
's requests
library when uploading a file or making an HTTP request
with a POST
that has a body
(aka payload
), it will calculate and set the Content-Length header (this is an integer
of the size in bytes of a request)
multipart/form-data
is most often used for file uploads with accompanying data/metadata and details in one single request, so it is extremely useful as a format and was built to be efficient.
Note
When sending the bytes
of a file, ensure that the raw binary data or bytes of the file are sent and that the file data is not base64
encoded. If you send base64
encoded file data as the bytes
then you may receive errors similar to, "The submitted data was not a file. Check the encoding type on the form."
Below is an example of what occurs at the HTTP level
(which all HTTP
requests are in the end) and what the programming languages and libraries are building when you send a multipart request:
POST /example/upload-endpoint HTTP/1.1
Content-Length: 1234
Content-Type: multipart/form-data; boundary=---abc12345678
---abc12345678
Content-Disposition: form-data; name="description"
Testing. 123. Testing.
---abc12345678
Content-Disposition: form-data; name="type"
INVOICE
---abc12345678
Content-Disposition: form-data; name="file"; filename="example.pdf"
Content-Type: application/pdf
(content of the uploaded file example.pdf as a string of bytes)
---abc12345678
Below are some examples showing how to perform file uploads simply using a multipart/form-data
in various programming languages. If you see \r\n
in examples like cURL
and you are not familiar, this is the computer equivalent of a new line and a carriage return (yes, like in the days of typewriters).
Upload a File with Multipart Form Data
If a file is located on a computer's file system or accessible via a file share or drive from that computer, then you may send the file directly using various helpers in programming languages.
Since a file is a set of bytes or data, you may also send the bytes of the file or with the knowledge that files are just bytes and data then you may save the bytes of a file (a byte array, byte stream, byte string, etc.) to a filesystem then send the file that way.
Uploading File Via Multipart Form Data
curl --location 'https://example.com/example/upload-endpoint' \
--header 'Content-Type: multipart/form-data' \
--header 'Authorization: YOUR_API_KEY_OR_AUTH_VALUE_HERE' \
--form 'file=@"/C:/Users/jdoe/temp/example.pdf"' \
--form 'type="INVOICE"' \
--form 'description="Testing 123."'
import requests
url = "https://example.com/example/upload-endpoint"
headers = {
"Authorization": "YOUR_API_KEY_OR_AUTH_VALUE_HERE"
}
payload = {
"description": "Testing 123.",
"type": "INVOICE"
}
bytes_of_file_or_file_like_object = open("path/to/file/example.pdf", "rb")
files = [
("file", ("example.pdf", bytes_of_file_or_file_like_object, "application/pdf"))
]
response = requests.request("POST", url, headers=headers, data=payload, files=files)
print(response.json())
import java.io.File;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
OkHttpClient client = new OkHttpClient();
File file; // this is your file object
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("description", "Testing 123.")
.addFormDataPart("file",
file.getName(),
RequestBody.create(MediaType.parse("application/pdf"), file))
.addFormDataPart("type", "INVOICE")
.build();
Request request = new Request.Builder()
.url("https://example.com/example/upload-endpoint")
.post(requestBody)
.header("Authorization", "YOUR_API_KEY_OR_AUTH_VALUE_HERE")
.header("Content-Type", "multipart/form-data")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
response.body().string();
}
Tip
The essence of a file is the bytes or data of the file, so if you have access to the bytes or data of a file you may write or persist that file to a location that you have write
permissions to such as the local filesystem of a computer or server (i.e. you can save files on a machine at a particular location). For some use cases, saving file bytes to disk (such as in a temp directory) and sending from there may be easier than sending the bytes directly.
Uploading File Bytes Via Multipart Form Data
Since files are just their bytes and data, they may also be uploaded without direct access to the file or need to re-read bytes of the file if you already have those bytes and data in memory.
Below are examples of sending multipart/form-data
data with a file upload using the file's bytes or data as part of the request.
curl 'https://example.com/example/upload-endpoint ' \
-H 'authorization: Token {replace_with_token}' \
-H 'content-type: multipart/form-data; boundary=---abc12345678' \
--data-raw $'---abc12345678\r\nContent-Disposition: form-data; name="description"\r\n\r\nTesting. 123.\r\n---abc12345678\r\nContent-Disposition: form-data; name="type"\r\n\r\nINVOICE\r\n---abc12345678\r\nContent-Disposition: form-data; name="file"; filename="example.pdf"\r\nContent-Type: application/pdf\r\n\r\n\(content of the uploaded file example.pdf as a string of bytes)\r\n---abc12345678--\r\n' \
--compressed
import requests
url = "https://example.com/example/upload-endpoint"
headers = {
"Authorization": "YOUR_API_KEY_OR_AUTH_VALUE_HERE"
}
payload = {
"description": "Testing 123.",
"type": "INVOICE"
}
# requests handles creating the file boundaries
# Also see the following if sending bytes: https://stackoverflow.com/a/74389385
# Sending a file or file bytes as part of a multipart form data request
# is the same for python. You may need to base64 decode a byte string
# from another system, decode a byte string into bytes, etc. depending on
# the format of how you received the bytes of the file
bytes_of_file_or_file_like_object = open("path/to/file/example.pdf", "rb")
files = [
("file", ("example.pdf", bytes_of_file_or_file_like_object, "application/pdf"))
]
response = requests.request("POST", url, headers=headers, data=payload, files=files)
print(response.json())
import java.io.File;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
OkHttpClient client = new OkHttpClient();
byte [] fileData;
// byte [] fileData = FileUtils.readFileToByteArray(file);
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("description", "Testing 123.")
.addFormDataPart("file",
"example.pdf",
RequestBody.create(MediaType.parse("application/pdf"), fileData))
.addFormDataPart("type", "INVOICE")
.build();
Request request = new Request.Builder()
.url("https://example.com/example/upload-endpoint")
.post(requestBody)
.header("Authorization", "YOUR_API_KEY_OR_AUTH_VALUE_HERE")
.header("Content-Type", "multipart/form-data")
.build();
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
response.body().string();
}