R2
R2は、Cloudflareのオブジェクトストレージです。 写真や帳票などのファイルを保存できます。 行と列で扱うデータは、Durable Objectsに保存してください。
バケッ トの仕組み
- R2のバケットは1組織につき1つです。
- バケットは組織管理者が管理画面で作成します。ZIPを登録してもバケットは作成されません。
- ワーカープラグインは、
wrangler.jsoncでr2_bucketsを宣言すると組織のバケットを使えます。 - バケットはワーカープラグインとは別に管理されます。宣言を外したり、ワーカープラグインを削除したりしても、バケットとファイルは残ります。
R2を使う手順
- 管理画面でR2のバケットを作成する
wrangler.jsoncにr2_bucketsを宣言するEnvの型にバインディングを追加する- ZIPを登録する
バケットの作成は、ワーカープラグイン管理を参照してください。 バケットはワーカープラグインを登録する前でも作成できます。
バケットを作成していない組織で、r2_bucketsを宣言したZIPを登録するとエラー(r2 bucket is not created)になります。
先にバケットを作成してから、ZIPを登録してください。
wrangler.jsonc
{
"r2_buckets": [{ "binding": "FILES", "bucket_name": "local-files" }]
}
r2_bucketsには1つだけ宣言できます。2つ以上宣言するとエラーになります。- 登録時に読み込むのは
bindingだけです。bucket_nameはローカルのpnpm dev用の名前です。登録時は無視されます。 bindingには、Durable Objectsのバインディングやシークレットと同じ名前を使えません。
Envの型
declare global {
interface Env {
FILES: R2Bucket
}
}
export {}
コードから使う
バケットはenvのバインディングから操作します。
APIはWorkers APIのR2と同じです。
// 保存する。本文はメモリに読み込まず、ストリームのまま渡す
app.put('/api/files/*', async (ctx) => {
let key: string
try {
key = decodeURIComponent(new URL(ctx.req.url).pathname.slice('/api/files/'.length))
} catch {
return ctx.json({ message: 'invalid key encoding' }, 400)
}
if (key === '') return ctx.json({ message: 'key is required' }, 400)
// 長さが分からないストリームはR2に渡せない
if (!ctx.req.header('Content-Length')) {
return ctx.json({ message: 'Content-Length is required' }, 411)
}
const object = await ctx.env.FILES.put(key, ctx.req.raw.body ?? new Uint8Array(), {
httpMetadata: { contentType: ctx.req.header('Content-Type') ?? 'application/octet-stream' },
})
return ctx.json({ key: object.key, size: object.size }, 201)
})
// 一覧を取得する。delimiterを指定すると、フォルダーの単位で取得できる
const result = await env.FILES.list({ prefix: 'photos/', delimiter: '/', limit: 100 })
// 取得する
const object = await env.FILES.get(key)
// 削除する
await env.FILES.delete(key)
ストリームのままput()に渡すには、リクエストにContent-Lengthが必要です。
ブラウザからFileを送る場合は、Content-Lengthが自動で付きます。
ファイルを返すときの注意
ワーカープラグインの応答は、metatell本体と同じオリジンで扱われます。 アップロードされたHTMLなどをそのまま返すと、metatell本体のオリジンでスクリプトを実行されるおそれがあります。 利用者がアップロードしたファイルを返すときは、次のヘッダーを付けてください。
return new Response(object.body, {
headers: {
'Content-Type': object.httpMetadata?.contentType ?? 'application/octet-stream',
'Content-Disposition': `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`,
'Content-Security-Policy': "default-src 'none'; sandbox",
},
})
ローカルで開発する
pnpm devでは、wrangler devがR2をローカルで再現します。
ファイルは.wrangler/に保存されます。
ローカルのバケットは組織のバケットとは別です。ローカルでアップロードしたファイルは本番に反映されず、本番のファイルもローカルには表示されません。
制限事項
| 項目 | 上限・扱い |
|---|---|
| バケットの数 | 1組織につき1つ |
| リクエストのボディ | 100MB(Cloudflare Workersの上限) |
| 管理画面からのアップロード | 1ファイル100MB |
| キーの長さ | 1,024バイト |
- 同じキーで保存すると、既存のファイルを上書きします。
- キーに
.や..の部分(a/../bなど)を含めると、URLのパスが正規化されるため、パスにキーを含めるAPIからは扱えなくなります。 - バケットにアクセスできるのは、組織のワーカープラグインと組織管理者だけです。ワーカープラグインのAPIを公開する場合は、トークンを検証し、利用者ごとにアクセスできるファイルを制限してください。
管理画面での操作
組織管理者は管理画面で、バケットの作成と削除、ファイルの一覧、ダウンロード、アップロード、削除ができます。
- 管理画面からのアップロードは、ワーカープラグインが
r2_bucketsを宣言しているときだけできます。 r2_bucketsの宣言を外した後も、残ったファイルの確認、ダウンロード、削除はできます。- ワーカープラグインが
r2_bucketsを宣言している間は、バケットを削除できません。
バケットを削除すると、中のファイルはすべて失われ、元に戻せません。
操作はワーカープラグイン管理を参照してください。