メインコンテンツまでスキップ

R2

R2は、Cloudflareのオブジェクトストレージです。 写真や帳票などのファイルを保存できます。 行と列で扱うデータは、Durable Objectsに保存してください。

バケットの仕組み​

  • R2のバケットは1組織につき1つです。
  • バケットは組織管理者が管理画面で作成します。ZIPを登録してもバケットは作成されません。
  • ワーカープラグインは、wrangler.jsoncでr2_bucketsを宣言すると組織のバケットを使えます。
  • バケットはワーカープラグインとは別に管理されます。宣言を外したり、ワーカープラグインを削除したりしても、バケットとファイルは残ります。

R2を使う手順​

  1. 管理画面でR2のバケットを作成する
  2. wrangler.jsoncにr2_bucketsを宣言する
  3. Envの型にバインディングを追加する
  4. ZIPを登録する

バケットの作成は、ワーカープラグイン管理を参照してください。 バケットはワーカープラグインを登録する前でも作成できます。

警告

バケットを作成していない組織で、r2_bucketsを宣言したZIPを登録するとエラー(r2 bucket is not created)になります。 先にバケットを作成してから、ZIPを登録してください。

wrangler.jsonc​

wrangler.jsonc
{
"r2_buckets": [{ "binding": "FILES", "bucket_name": "local-files" }]
}
  • r2_bucketsには1つだけ宣言できます。2つ以上宣言するとエラーになります。
  • 登録時に読み込むのはbindingだけです。bucket_nameはローカルのpnpm dev用の名前です。登録時は無視されます。
  • bindingには、Durable Objectsのバインディングやシークレットと同じ名前を使えません。

Envの型​

src/env.d.ts
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を宣言している間は、バケットを削除できません。
危険

バケットを削除すると、中のファイルはすべて失われ、元に戻せません。

操作はワーカープラグイン管理を参照してください。