Uploads

Introduction

endpoints

_10
# Uploads
_10
POST https://api.ittybit.com/uploads
_10
_10
# Upload URLs
_10
POST https://yourdomain.com/store/single?key=[UPLOAD_KEY]
_10
PUT https://yourdomain.com/store/resumable?key=[UPLOAD_KEY]
_10
POST https://yourdomain.com/store/resumable?key=[UPLOAD_KEY]

Uploads are short-lived sessions that allow you your users to create new Media Items with a direct file upload from your client to our servers.


How To Upload Files

1) Create an Upload Session

simple-upload.js
resumable-upload.js

_10
const BASE_URL = "https://api.ittybit.com";
_10
_10
const response = await fetch(`${BASE_URL}/uploads`, {
_10
headers: {
_10
method: "POST",
_10
Authorization: `Bearer ${env.ITTYBIT_API_KEY}`,
_10
},
_10
});

simple-response.json
resumable-response.json

_16
{
_16
"meta":{
_16
"id": "req_abcdefgh1234",,
_16
"method": "POST",
_16
"url": "/uploads",
_16
"version": "2024-03-21",
_16
"status": 200,
_16
"org_id": "org_abcdefgh1234",
_16
"project_id": "prj_abcdefgh1234",
_16
"live_mode": false,
_16
},
_16
"data": {
_16
"url": "https://yourdomain.com/store/single?key=aWQ9QU...aWQ9QU==",
_16
},
_16
"links": {}
_16
}

First, make a POST request to create a new upload session.

This will return an upload URL, that you can then use to upload your file directly from the client to our servers.

You should do this initial request from your server to keep your API keys secret.

The returned upload url will look something like this: https://yourdomain.com/store/single?key=aWQ9QU....

Endpoint

POST /uploads

Body Parameters

  • resumable boolean

    Whether to initiate a resumable upload session. If this is not included you will create a simple upload session. Default is false.

  • conditions object

    A set of conditions that the media must meet to be accepted. See below for more details.

  • alias string

    A unique alias for the media. This will be used to generate the media URL. It should be unique to your project. It can contain letters, numbers, and hyphens. If not provided, we will use the default URL in the format /:media_id/original.

  • metadata object

    A set of key-value pairs to store with the media. This can be used to store any additional information you need to associate with the media. For example, you might want to store the original filename, the user who uploaded the media, or any other data that is relevant to your project. Valid values are strings, numbers, booleans, and null. Nested objects are not supported.


2) Upload the File

Use the new upload URL to upload your file directly from the client to our servers.

There are two upload methods:

  • Simple: A simple upload method sends the entire file in a single request. This method is best for small files. File size limit is 64MB.

  • Resumable: A resumable upload method that allows you to upload large files in smaller chunks. This method is best for large files. The file size limit is 1GB per file, but you can contact us to increase this limit (upto 5TB per file).


a) Simple Upload Method

simple-upload.js

_14
_14
const { data } = await response.json();
_14
_14
const file = document.getElementById("file").files[0];
_14
_14
const result = await fetch(data.url, {
_14
headers: {
_14
method: "POST",
_14
"Content-Type": "multipart/form-data",
_14
body: new FormData({
_14
file: file,
_14
}),
_14
},
_14
});

In a simple upload the file is passed to our server in one POST request.

The Content-Type is multipart/form-data which allows you to send a File directly.

Your upload URL is already authorized to add one file to your project, so no additional Authorization headers are needed. You should not pass your Ittybit API Key to the client.

The only body parameter is the file itself. You can access the file from a file input element using document.getElementById("file").files[0] (assuming the input has id="file").


b) Resumable Upload Method

resumable-upload.js

_66
_66
const { data } = await response.json();
_66
const url = data.url;
_66
_66
const CHUNK_SIZE = 1024 * 1024 * 10; // #B
_66
const file = document.getElementById("file").files[0];
_66
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
_66
_66
async function readFileAsArrayBuffer(file) {
_66
return new Promise((resolve, reject) => {
_66
let reader = new FileReader();
_66
reader.onload = event => resolve(event.target.result);
_66
reader.onerror = error => reject(error);
_66
reader.readAsArrayBuffer(file);
_66
});
_66
}
_66
_66
async function uploadFile({ file, url }) {
_66
let parts = [];
_66
let arrayBuffer = await readFileAsArrayBuffer(file);
_66
_66
for (let i = 0; i < totalChunks; i++) {
_66
let start = i * CHUNK_SIZE;
_66
let end = Math.min(file.size, start + CHUNK_SIZE);
_66
let chunk = arrayBuffer.slice(start, end); // #A
_66
_66
// #C
_66
let response = await fetch(url, {
_66
method: 'PUT',
_66
headers: {
_66
'Content-Type': 'application/octet-stream', // #D
_66
'X-Upload-Index': i, // #E
_66
},
_66
body: chunk
_66
});
_66
_66
if (!response.ok) {
_66
throw new Error('Upload failed');
_66
}
_66
_66
let { data } = await response.json();
_66
parts.push(data); // #F
_66
_66
console.log(`Chunk ${i + 1} of ${totalChunks} uploaded`);
_66
}
_66
_66
// #G
_66
let response = await fetch(url, {
_66
method: 'POST'
_66
headers: {
_66
'Content-Type': 'application/json',
_66
body: JSON.stringify({ parts })
_66
}
_66
});
_66
_66
if (!response.ok) {
_66
throw new Error('Upload completion failed');
_66
}
_66
_66
console.log('Upload complete');
_66
_66
let result = await response.json();
_66
return result;
_66
}
_66
_66
const result = await uploadFile({ file, url });

In a resumable upload the file is split up and passed to our server in multiple smaller chunks.

This allows you to upload much larger files more robustly, to quickly restart an upload if it is interrupted (without aborting the whole upload and starting again), and even to manually pause and resume uploads.

First, you need to split the file. You can do this using the FileReader API to read the file as an ArrayBuffer and then slice it into chunks (#A). In our example we use 10MB chunks (#B).

Then, we loop through the chunks and send each one to the upload url in a PUT request (#C).

As we are sending bytes directly in the body we will set the Content-Type header to application/octet-stream (#D). We also pass a custom X-Upload-Index header containing the index of the chunk (#E). This allows our server to reassemble the file in the correct order even if the chunks arrive out of order due to network issues.

Each PUT request will return a data object containing a PartObject which looks like this:


_10
{
_10
index: 0,
_10
etag: "5aaf2....",
_10
}

We store these PartObjects in an array (#F) and send them to the server in a final POST request to complete the upload (#G).

The POST request will return a data object which contains the media details.

Note 1 This is a simplified example. In a real-world app we would handle errors, retries, and display upload progress. There is a guide coming very soon!

Note 2 If you use React on your frontend we can provide an <Upload /> component that handles all of this for you. Contact us for more information. If you are using a different frontend framework, please get in touch and we'll be happy to work with you to create a similar solution.


3) Optional: Save Media Details

result.json

_43
{
_43
"meta":{
_43
"id": "req_abcdefgh1234",,
_43
"status": 200,
_43
"org_id": "org_abcdefgh1234",
_43
"project_id": "prj_abcdefgh1234",
_43
"live_mode": false,
_43
"object": "media",
_43
},
_43
"data": {
_43
"id": "med_abcdefgh1234",
_43
"object": "media",
_43
"kind": "video",
_43
"width": 1920,
_43
"height": 1080,
_43
"duration": 60,
_43
"filesize": 12345678,
_43
"alias": "usr_abcd/profile/video",
_43
"url": "https://yourdomain.com/usr_abcd/profile/video/",
_43
"sources": [
_43
{
_43
"id": "src_abcdefgh1234",
_43
"object": "source",
_43
"label": "original",
_43
// ... Source object props
_43
// https://ittybit.com/docs/api/sources
_43
"url": "https://yourdomain.com/med_abcdefgh1234/original",
_43
}
_43
],
_43
"intelligence": [],
_43
"metadata": {
_43
"filename": "original-filename.mp4",
_43
"user_id": "usr_abcd",
_43
},
_43
"created": "2024-03-30T13:00:00Z",
_43
"updated": "2024-03-30T13:00:00Z",
_43
"status": "active"
_43
},
_43
"links": {
_43
"self": "/media/med_abcdefgh1234",
_43
"parent": "/media",
_43
}
_43
}

After the upload is complete, you can save the media details – or a subset such as the media's id and url – to your database or CMS.

A successful response will contain a full Media object, and the Source object for the original file.

We automatically analyse each upload and will include some file properties for the new Source:

  • kind string

    The type of media. Possible values are image, video, or audio.

  • mimetype string

    The MIME type of the media. For example, image/jpeg, video/mp4, audio/mpeg.

  • width integer

    The width of the media in pixels. For audio files this will be null.

  • height integer

    The height of the media in pixels. For audio files this will be null.

  • duration number

    The duration of the media in seconds. Only available for videos and audio files.

  • fps number

    The frames per second of the media. Only available for videos and animated images.

  • filesize integer

    The size of the media in bytes.

  • bitrate integer

    The bitrate of the media in kbps. For images this will be null.


Upload Conditions

You can set conditions on your upload to ensure that only media that meets your requirements is accepted.

conditions.js

_14
const FILESIZE_LIMIT = 100*1024*1024; // 100MB
_14
const MAX_LENGTH = 1920; // px
_14
const MAX_DURATION = 5 * 60; // 5 minutes
_14
_14
const conditions = {
_14
operator: "&&",
_14
rules: [
_14
{ key: "kind", operator: "==", value: "video" },
_14
{ key: "filesize", operator: "<=", value: FILESIZE_LIMIT },
_14
{ key: "width", operator: "<=", value: MAX_LENGTH },
_14
{ key: "height", operator: "<=", value: MAX_LENGTH },
_14
{ key: "duration", operator: "<=", value: MAX_DURATION }
_14
],
_14
}

Body Parameters

  • operator string

    The operator to use to combine the rules. Possible values are && (AND) / || (OR).

  • rules array

    An array of rules to apply to the media. Each rule is an object with the following properties:

    • key string

      The key of the media property to check. Possible values are kind, mimetype, filesize, width, height, duration.

    • operator string

      The operator to use to compare the value. Possible values are ==, !=, >, <, >=, <=.

    • value varies

      The value to compare the media property to. The type of this value depends on the key and operator.

Note You will still be charged for an upload (1x the filesize against your monthly usage limit) even if the media is rejected on the server-side. So it's best to also validate the media on the client-side before starting the upload. Server-side conditions give you an extra layer of security and additional options that aren't possible on the client-side.