章と記事の一覧
動かすストレージ
R2
S3 互換のオブジェクトストレージ。転送無料
クラウドインフラの請求書を受け取ったエンジニアが、思わず溜息をつく項目がある。Egress(外部への下りデータ転送)課金である。ストレージそのものの維持費がどれほど安くとも、大量の画像や動画がダウンロードされるたびにネットワーク転送料が雪だるま式に膨らんでいく。
Cloudflare R2 は、この下りデータ転送料金を課さない。どれほどファイルが読み出されようと、R2 から直接送り出す転送量に対する請求はゼロである。
KV と R2 の棲み分け
| 比較軸 | Workers KV | Cloudflare R2 |
|---|---|---|
| 最小単位 | キーと値(テキスト/バイナリ) | オブジェクト(完全なファイル) |
| 容量制限 | 1 キーあたり最大 25MB | 1 オブジェクトあたり最大 5TB |
| 最適用途 | 設定値、フラグ、短寿命キャッシュ | 画像、動画、ログ、バックアップアーカイブ |
| 課金構造 | 読み取り・書き込み・削除・一覧の回数 + 保存データ容量 | 保存データ容量 + 操作リクエスト回数(転送料金なし) |
開発において押さえるべき作法
- 基本操作は
put(key, streamOrBuffer)、get(key)、delete(key)、list({ prefix })である。 - 取得したオブジェクトの本文を取り出す際は、用途に応じて
.text()、.arrayBuffer()、あるいはストリームを扱う.bodyを呼び分ける。 - 大きなファイルをバッファに読み込むと、Worker のメモリを一気に消費する。100MB 程度までは、長さの分かる
ReadableStream(Content-Length付きのリクエストボディかFixedLengthStream)をput()に渡す。それを超えるファイルは、マルチパートアップロード(createMultipartUpload())を使う。 - 署名付き URL(Presigned URL)を使うと、Worker のメモリと帯域を経由せず、ブラウザから直接 R2 へアップロード・ダウンロードできる。R2 のバインディングは署名付き URL を作れないため、S3 互換 API の認証情報(Access Key ID と Secret)で署名する(
aws4fetchなど)。有効期限は最大 7 日で、ブラウザから使うなら CORS の設定が要る。カスタムドメインでは使えない。
エディタ上の env.STORAGE は、実際の R2 を RPC で中継したもので、本番のバインディングとは異なる。put() は文字列を保存し、get() は本文の文字列(なければ null)を返すため .text() は呼べない。list() は接頭辞の文字列({ prefix } ではない)を受け取り、キーとサイズを並べた文字列(例: lesson/hello.txt (37B))の配列を返す。保存先は全員で共有している。
export default {
async fetch(request, env) {
// オブジェクトの書き込み / Put objects
await env.STORAGE.put("lesson/hello.txt", "はじめての R2 オブジェクト");
await env.STORAGE.put("lesson/notes/1.txt", "メモ 1");
// 読み出し / Get object
const text = await env.STORAGE.get("lesson/hello.txt");
console.log("読み出し:", text);
// プレフィックス一覧取得 / List objects by prefix
const list = await env.STORAGE.list("lesson/");
console.log("一覧:", list);
// 削除 / Delete object
await env.STORAGE.delete("lesson/notes/1.txt");
console.log("削除後:", await env.STORAGE.list("lesson/"));
return Response.json({ text, list, afterDelete: await env.STORAGE.list("lesson/") });
},
};
実行結果
実行するとここに結果が出ます。ネットワークは塞がれていますが、D1・KV・R2・Durable Object の状態は訪問者全員で共有されます。