Direct Creator Uploads を使うと、API トークンをクライアントに公開せず、エンドユーザーが Cloudflare Stream へ直接動画をアップロードできます。実装方法は 基本 POST リクエスト と tus プロトコル のいずれかです。次の図で、使う方法を判断します。
flowchart LR
accTitle: Direct Creator Uploads の判断フロー
accDescr: ファイルサイズと接続の安定性に応じて、基本 POST と tus プロトコルのどちらを使うかを決めるフローです。
A{"動画は 200 MB を超えますか?"}
A -->|はい| B["tus プロトコルを使う必要があります"]:::link
A -->|いいえ| C{"エンドユーザーの接続は安定していますか?"}
C -->|はい| D["基本 POST を推奨します"]:::link
C -->|いいえ| E["tus プロトコルは任意ですが、推奨します"]:::link
classDef link text-decoration:underline,color:#F38020
click B "#direct-creator-uploads-with-tus-protocol" "tus プロトコルの説明"
click D "#basic-post-request" "基本 POST の手順"
click E "#direct-creator-uploads-with-tus-protocol" "tus プロトコルの説明"
エンドユーザーの動画が 200 MB 未満で、接続が安定している場合は、この方法を推奨します。接続が不安定な場合は、代わりに tus プロトコル を推奨します。
POST リクエストで Direct Creator Uploads を有効にする手順です。
Direct upload API で、一意で 1 回限りのアップロード URL を生成します。
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": []
}外部アプリケーションから REST API を使う方法と、TypeScript、Python、Go 向けの事前生成 SDK の詳細は、Stream の REST API と SDK リファレンス を参照してください。
export default {
async fetch(request, env, ctx): Promise<Response> {
const directUpload = await env.STREAM.createDirectUpload({
maxDurationSeconds: 3600,
});
return new Response(JSON.stringify(directUpload));
},
} satisfies ExportedHandler<{ STREAM: StreamBinding }>;{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "<ENTER_WORKER_NAME>",
"main": "src/index.ts",
"compatibility_date": "$today",
"observability": {
"enabled": true
},
"stream": {
"binding": "STREAM"
}
}Workers Stream binding API リファレンス を参照してください。
前のステップの uploadURL を使い、ユーザーは 200 MB までの動画ファイルをアップロードできます。次のリクエスト例を参照してください。
curl --request POST \
--form file=@/Users/mickie/Downloads/example_video.mp4 \
https://upload.videodelivery.net/f65014bc6ff5419ea86e7972a047ba22成功すると HTTP ステータス 200 が返ります。作成時に定義した制約を満たさない、または 200 MB を超える場合は、4xx が返ります。
動画が 200 MB を超える場合は、tus プロトコルが必須です。200 MB 未満でも、接続が不安定になり得る場合は、再開できるため tus プロトコルを推奨します。tus の要件、クライアント例、アップロードオプションの詳細は、再開可能な大容量ファイル(tus) を参照してください。
この 2 ステップの流れは、次の図のとおりです。
sequenceDiagram accTitle: tus による Direct Creator Uploads のシーケンス図 accDescr: バックエンドが tus アップロード URL を発行し、エンドユーザーが Stream へ直接アップロードする 2 ステップの流れです。 participant U as エンドユーザー participant B as バックエンド participant S as Cloudflare Stream U->>B: アップロードを開始する B->>S: tus アップロード URL を要求する(認証あり) S->>B: 1 回限りのアップロード URL を返す B->>U: 1 回限りのアップロード URL を返す U->>S: tus で動画を直接アップロードする
次の例は、エンドユーザーに1 回限りのアップロード URL を返す Worker の作り方です。tus プロトコルのアップロードでは、バックエンドが Tus-Resumable、Upload-Length、Upload-Metadata ヘッダーを渡す必要があります。1 回限りのアップロード URL はレスポンス本文ではなく、Location ヘッダーで返ります。
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,
},
});
}tus クライアントから、バックエンドのエンドポイントを直接使います。ステップ 1 のバックエンドを uppy tus クライアントと組み合わせる完全な例は、次のとおりです。
<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>tus の詳細とクライアントコード例は、再開可能な大容量ファイル(tus) を参照してください。
tus を使う場合も、basic upload の Direct Creator Upload と 同じ制約 を適用できます。そのためには、最初のリクエスト(上の例では Worker が行うリクエスト)の Upload-Metadata リクエストヘッダーに expiry と maxDurationSeconds を含めます。実際のファイルアップロードを行う後続リクエストでは、Upload-Metadata の値は無視されます。
Upload-Metadata ヘッダーにはキーと値のペアを入れます。キーはテキスト、値は base64 でエンコードします。キーと値は等号ではなく、スペースで区切ります。複数のペアを連結するときは、余分なスペースなしのカンマを使います。
次の例では、Upload-Metadata ヘッダーが Stream に対し、最大再生時間 10 分、有効期限タイムスタンプより前のアップロードのみを受け付け、この動画を非公開にするよう指示しています。
'Upload-Metadata: maxDurationSeconds NjAw,requiresignedurls,expiry MjAyNC0wMi0yN1QwNzoyMDo1MFo='
NjAw は "600"(10 分)を base64 エンコードした値です。
MjAyNC0wMi0yN1QwNzoyMDo1MFo= は "2024-02-27T07:20:50Z"(RFC3339 形式のタイムスタンプ)を base64 エンコードした値です。
一意で 1 回限りのアップロード URL を作成したら、レスポンスの一意の識別子(uid)を保持し、ユーザーのアップロード進捗を追跡します。
進捗は次の方法で追跡できます。
-
uidを指定して 動画詳細の取得 API を使う -
webhook サブスクリプションを作成する と、動画ステータスの通知を受け取れます。通知には
uidが含まれます。