Direct creator uploads
Direct creator uploads let your end users upload videos directly to Cloudflare Stream without exposing your API token to clients.
-
If your video is a basic upload under 200 MB and users do not need resumable uploads, generate a URL that accepts an HTTP post request.
-
If your video is over 200 MB or if you need to allow users to resume interrupted uploads, generate a URL using the tus protocol.
In either case, you must specify a maximum duration to reserve for the user's upload to ensure it can be accommodated within your available storage.
Use this option if your users upload videos under 200 MB, and you do not need to allow resumable uploads.
- Generate a unique, one-time upload URL using the Direct upload API.
curl https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/direct_upload \--header 'Authorization: Bearer <API_TOKEN>' \ --data '{ "maxDurationSeconds": 3600 }'
{ "result": { "uploadURL": "https://upload.videodelivery.net/f65014bc6ff5419ea86e7972a047ba22", "uid": "f65014bc6ff5419ea86e7972a047ba22" }, "success": true, "errors": [], "messages": []}
- With the
uploadURL
from the previous step, users can upload video files that are limited to 200 MB in size. Refer to the example request below.
curl --request POST \ --form file=@/Users/mickie/Downloads/example_video.mp4 \ https://upload.videodelivery.net/f65014bc6ff5419ea86e7972a047ba22
A successful upload will receive a 200
HTTP status code response. If the upload does not meet
the upload constraints defined at time of creation or is larger than 200 MB in size, you will receive a 4xx
HTTP status code response.
- Create your own API endpoint that returns an upload URL.
The example below shows how to build a Worker to get a URL you can use to upload your video. The one-time upload URL is returned in the Location
header of the response, not in the response body.
export async function onRequest(context) { const { request, env } = context; const { CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN } = env; const endpoint = `https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/stream?direct_user=true`;
const response = await fetch(endpoint, { method: "POST", headers: { Authorization: `bearer ${CLOUDFLARE_API_TOKEN}`, "Tus-Resumable": "1.0.0", "Upload-Length": request.headers.get("Upload-Length"), "Upload-Metadata": request.headers.get("Upload-Metadata"), }, });
const destination = response.headers.get("Location");
return new Response(null, { headers: { "Access-Control-Expose-Headers": "Location", "Access-Control-Allow-Headers": "*", "Access-Control-Allow-Origin": "*", Location: destination, }, });}
- Use this API endpoint directly in your tus client. A common mistake is to extract the upload URL from your new API endpoint, and use this directly. See below for a complete example of how to use the API from Step 1 with the uppy tus client.
<html> <head> <link href="https://releases.transloadit.com/uppy/v3.0.1/uppy.min.css" rel="stylesheet" /> </head> <body> <div id="drag-drop-area" style="height: 300px"></div> <div class="for-ProgressBar"></div> <button class="upload-button" style="font-size: 30px; margin: 20px"> Upload </button> <div class="uploaded-files" style="margin-top: 50px"> <ol></ol> </div> <script type="module"> import { Uppy, Tus, DragDrop, ProgressBar, } from "https://releases.transloadit.com/uppy/v3.0.1/uppy.min.mjs";
const uppy = new Uppy({ debug: true, autoProceed: true });
const onUploadSuccess = (el) => (file, response) => { const li = document.createElement("li"); const a = document.createElement("a"); a.href = response.uploadURL; a.target = "_blank"; a.appendChild(document.createTextNode(file.name)); li.appendChild(a);
document.querySelector(el).appendChild(li); };
uppy .use(DragDrop, { target: "#drag-drop-area" }) .use(Tus, { endpoint: "/api/get-upload-url", chunkSize: 150 * 1024 * 1024, }) .use(ProgressBar, { target: ".for-ProgressBar", hideAfterFinish: false, }) .on("upload-success", onUploadSuccess(".uploaded-files ol"));
const uploadBtn = document.querySelector("button.upload-button"); uploadBtn.addEventListener("click", () => uppy.upload()); </script> </body></html>