本文へ移動
runlot

署名付き URL

ログインしていないユーザーでも限られた時間だけ利用できる URL を生成します。アップロードとダウンロードの両方に対応します。

const url = await env.storage.presign("covers/hello.png");

既定のリクエストメソッドは GET、有効期限は 300 秒 です。

await env.storage.presign(key, { method: "GET", ttl: 60 });    // 1 分間ダウンロード可能
await env.storage.presign(key, { method: "PUT", ttl: 3600 });  // 1 時間アップロード可能

ttl は秒単位で、最大 3,600 秒(1 時間) です。この値を超えるとリクエストを拒否します。

ブラウザから直接アップロードする構成

ワーカーは URL を発行するだけで、ファイルのデータはワーカーを経由しません。

// ワーカー
app.post("/upload-url", async (c) => {
  const { name, type } = await c.req.json();
  const key = `uploads/${crypto.randomUUID()}/${name}`;
  const url = await c.env.storage.presign(key, { method: "PUT", ttl: 600 });
  return Response.json({ key, url });
});
// ブラウザ
const { key, url } = await fetch("/upload-url", {
  method: "POST",
  body: JSON.stringify({ name: file.name, type: file.type }),
}).then((r) => r.json());

await fetch(url, { method: "PUT", body: file });

キーはワーカーで生成することが重要です。クライアントから渡されたファイル名をそのままキーに使うと、他のユーザーのファイルを上書きできてしまいます。

アップロードのサイズを固定します

size を渡すと、そのバイト数が署名に組み込まれます。異なる長さのアップロードはストレージバックエンドが 403 で拒否するため、バイトがワーカーを通らなくても上限が保たれます。

const url = await env.storage.presign(key, { method: "PUT", size: file.size, ttl: 600 });

size がなければ、URL ひとつでアップロードできる量に上限はありません。渡すと、URL の発行時に既使用量だけでなくそのサイズも含めて割り当てと照合します。

ブラウザは正確にそのバイト数を送る必要があります。同じ URL で別のファイルを送ると失敗するため、ファイルが変わるたびに URL を取り直してください。

GET と PUT のみ対応します

DELETE リクエスト用の署名付き URL は作成できません。削除は常に、ワーカーで権限を確認したうえで実行してください。漏れた URL ひとつがファイルの削除権限になることを防ぐためです。

ストレージバックエンドによる制限

署名付き URL はオブジェクトストレージのバックエンドでのみ利用できます。ローカル開発環境のようにディスクバックエンドを使用している場合は、501 エラー(unsupported)を返します。

ローカルで署名付き URL を使う流れをテストしたい場合は、ワーカーがファイルを直接返す代替経路を用意できます。

let url: string;
try {
  url = await env.storage.presign(key, { ttl: 600 });
} catch {
  url = `/files/${encodeURIComponent(key)}`; // ワーカーが get でファイルを返す経路
}

クォータ超過時の動作

書き込み用の署名付き URL は、ストレージ容量がいっぱいになると発行されません(507)。読み取り用の URL は引き続き発行できます。ファイルを削除して容量を確保できる必要があるためです。「いっぱい」の意味はプランによって異なります。クォータ を参照してください。

公開プレフィックス

署名付き URL は、ひとりがしばらくのあいだ読めればよいファイルに向いた道具です。サイトの画像、アップロードされたアバター、ダウンロードファイルのように、誰でもいつまでも読めるべきファイルに対して、アドレスごとに署名を付けるのは向いていません。代わりにプレフィックスを公開として登録すると、その配下のすべてのキーを、署名も有効期限もなしに通常の HTTPS GET で読み取れます。

runlot storage public add assets
runlot storage public list
runlot storage public rm assets

同じ 3 つの操作が、ダッシュボードの ファイルストレージ タブの「公開プレフィックス」カードにあります。公開と解除には admin ロールが必要で、一覧の閲覧は viewer で行えます。

プレフィックスは正規化された形で保存されます。assets は assets/ になり、照合はキーに対する単純な文字列プレフィックス比較です。末尾のスラッシュがあるため、assets/ が assets-private/ まで開いてしまうことはありません。/ だけを指定するとプロジェクトのすべてのキーが公開されます。空のプレフィックスは拒否されるため、空の引数が「すべて」を意味することはありません。1 つのプロジェクトで公開できるプレフィックスは 20 件までです。

アドレス

https://objects.runlot.io/<org>/<project>/<key>

組織 acme のプロジェクト shop にあるキー assets/logo.png は https://objects.runlot.io/acme/shop/assets/logo.png にあります。この経路で処理するのは GET と HEAD だけです。署名なしでは何も書き込めません。

ワーカーのコードから

env.storage.publicUrl(key) がそのアドレスを返し、キーが公開プレフィックスの配下にない場合は null を返します。

const url = env.storage.publicUrl("assets/logo.png"); // "https://objects.runlot.io/acme/shop/assets/logo.png"
const none = env.storage.publicUrl("private/report.pdf"); // null

同期関数で、リクエストは送りません。ワーカーの起動時に受け取ったプレフィックス一覧から文字列を連結するだけです。誰も公開していないキーに null が返るのはエラーではなく、通常の答えです。存在しないキーに get が null を返すのと同じ形です。put が拒否するキー(..、空のセグメント)はここでも例外を投げるため、不正なキーが <img src> に入るもっともらしい URL になることはありません。

公開プレフィックスを変更すると、新しい一覧を反映するためにワーカーが再起動されます。変更前に起動したワーカーは、実際には公開されているキーに null を返すことはあっても、公開されていないキーにアドレスを返すことはありません。実際に配信するかどうかはこの値ではなく、オブジェクトフロントが決めます。

公開ファイルと一緒に送られるヘッダー

ヘッダー値
Cache-Controlpublic, max-age=3600
ETag、Content-Type、Content-Lengthオブジェクトの値
Last-Modified分かる場合はオブジェクトの値

キャッシュの有効期間は一律 1 時間で、immutable は決して付きません。上書きしたキーは 1 時間以内に新しい内容を表示します。より長くキャッシュしたい場合は、キーに内容のハッシュを含め、内容が変わったら新しいキーにアップロードしてください。

署名なしでは決して配信しないもの

どの公開プレフィックスにも属さないキーは、署名なしでは決して配信されず、応答は 403 ではなく 404 です。非公開のキーと存在しないキーは外からは同じに見えるため、非公開ファイルのアドレスからは何も分かりません。バケット自体は常に非公開のままです。公開読み取りは、当社のフロントが一覧を確認したうえで当社の認証情報で行うため、ストレージバックエンド自体のポリシーが変わっても公開範囲が広がることはありません。

公開の解除は 60 秒以内に反映されます。フロントが一覧をその時間だけキャッシュするため、すでにアドレスを持っている側は runlot storage public rm が完了した後も最大 1 分間は読み取りを続けられます。

署名付き URL はこれらの影響を受けません。署名付きリクエストは公開一覧を参照する前に処理されるため、プレフィックスを公開しても署名付き URL の動作は変わりません。

このページの目次