はじめに
エクスポートサービスでは、保持対象のドキュメントを削除せずに一時的なダウンロードを削除する必要があります。この実験では、新しい非公開バケットに対して、プレフィックス単位の有効期限、ストレージクラス移行、未完了アップロードのクリーンアップルールを適用し、実際のポリシーとオブジェクトメタデータを確認します。
最初に、オブジェクト管理とマルチパートクリーンアップを完了してください。この新しい VM では、Node.js 22.22.0、Wrangler 4.131.1、AWS SDK 3.888.0 を使用します。R2 が有効になっており、新しいバケットを設定する権限が必要です。ライフサイクルの動作と料金を確認してください。特に、Infrequent Access の最低保存期間と取得料金に注意してください。この実験で作成するデータは Standard ストレージに保持し、セッション中に明示的に削除します。合否チェックでは、数日後の削除や移行ではなく、適用されたルールと現在のメタデータを確認します。ドメインは必要ありません。
非公開のドキュメントバケットを作成する
このステップでは、この VM を認証し、一時的に使用するバケットを 1 つ作成します。デバイス認証によって、学習用アカウントを確認します。R2 バケットの管理には、そのアカウントだけに制限した別の API トークンを使用します。
以下のコマンドで使用する構文に合わせて Bash を起動し、準備済みのプロジェクトへ移動してツールを確認します。リソース名の変数を引き続き使用できるよう、同じターミナルを開いたままにしてください。
bash
cd /home/labex/project/r2-lab
export PATH="$PWD/.tools/node-v22.22.0-linux-x64/bin:$PATH"
node --version
npx wrangler --version
表示されたデバイスコードを、自分のブラウザーで認証します。同意する前に、学習用アカウントと、要求されているアカウントおよびユーザーの読み取りスコープを確認してください。
npx wrangler login --device --browser=false --scopes account:read user:read
npx wrangler whoami --json
loggedIn: true になっていることを確認します。アカウントが 1 つだけ表示される場合でも、アカウント名を確認してください。以下の YOUR_ACCOUNT_ID を、そのアカウントの実際の 32 文字の ID に置き換えます。openssl rand -hex 6 は 12 個のランダムな 16 進数文字を生成するため、この実験のバケット名が以前の実行と重複しません。以下のヒアドキュメントで標準の設定ファイルを作成します。シェルは変数をその内容に置き換えます。
ACCOUNT_ID=YOUR_ACCOUNT_ID
RUN_ID=$(openssl rand -hex 6)
NAME="labex-c05-r07-$RUN_ID"
BUCKET="$NAME-docs"
cat > wrangler.jsonc <<JSON
{"name":"$NAME","account_id":"$ACCOUNT_ID","compatibility_date":"2026-07-30","r2_buckets":[{"binding":"DOCUMENTS","bucket_name":"$BUCKET"}]}
JSON
バケット管理用に、Cloudflare プロフィールの API Tokens ページを開き、この実験の名前を付けたカスタムトークンを作成します。Account → Workers R2 Storage → Edit を許可し、Account Resources を、保存した ID の学習用アカウントだけに制限します。短い有効期限を設定してください。他のアカウントや無関係な権限は含めないでください。この管理トークンは、作成や削除などのバケット管理に使用します。このステップの後半では、S3 SDK でオブジェクトを操作するために、このバケットに権限を限定した別のオブジェクトトークンを作成します。
トークンは 1 回だけ、この非表示の VM プロンプトに入力します。umask 077 によってファイルを自分のユーザーだけが読み取れるようにし、read -s によって入力を非表示にします。このファイルでは Wrangler の標準トークン変数を使用し、Git の対象外になります。
umask 077
read -r -s -p 'R2 management API token: ' R2_MANAGEMENT_TOKEN; printf '\n'
printf 'CLOUDFLARE_API_TOKEN=%s\n' "$R2_MANAGEMENT_TOKEN" > .env.management
unset R2_MANAGEMENT_TOKEN
--env-file=.env.management は R2 管理コマンドにだけ使用します。通常の whoami では、引き続き VM のデバイス認証を確認します。
--env-file は各 Wrangler コマンドの末尾に置きます。これにより、ファイル引数のリストにコマンド名が取り込まれるのを防げます。各バケットの作成後、Wrangler が設定へのバインディング追加を尋ねたら、n を入力して Enter を押してください。必要なバインディングはすでに設定されています。
npx wrangler r2 bucket create "$BUCKET" --env-file=.env.management
バケット一覧を表示し、生成された正確な名前を確認します。他のバケットは別の作業で使用しているため、変更しないでください。
npx wrangler r2 bucket list --env-file=.env.management
Dashboard で Storage & databases → R2 → Overview を開き、この正確な名前のバケットを選択して、オブジェクト一覧が空であることを確認します。設定画面では、パブリック開発 URL とカスタムドメインを無効のままにします。Dashboard に表示されるバケット名で対象を確認できます。保存されたバイト列については、後のダウンロード確認で検証します。
S3 互換 API を使用すると、標準のストレージ SDK から R2 にアクセスできます。Wrangler のデバイストークンとは別に、アクセスキーのペアを使用します。R2 の Overview で Account Details → API Tokens → Manage を開き、この実験で生成したリソース名を付けた User API token を作成します。Object Read & Write を選択し、この新しいバケットだけにアクセスを制限します。フォームに項目がある場合は、短い有効期限を選択してください。すべてのバケットや Admin アクセスは選択しないでください。1 回だけ表示されるシークレットを保存するまで、このトークンのページを開いたままにします。
VM で次の Bash プロンプトを使用します。read -s は入力を非表示にし、umask 077 は認証情報ファイルを自分のユーザーだけが読み取れるようにします。これらの名前は AWS SDK の標準環境変数です。Access Key ID と Secret Access Key を、それぞれのプロンプトに貼り付けて Enter キーを押します。一般的な API トークンの値は貼り付けないでください。
umask 077
read -r -s -p 'Access Key ID: ' AWS_ACCESS_KEY_ID; printf '\n'
read -r -s -p 'Secret Access Key: ' AWS_SECRET_ACCESS_KEY; printf '\n'
printf 'AWS_ACCESS_KEY_ID=%s\nAWS_SECRET_ACCESS_KEY=%s\n' "$AWS_ACCESS_KEY_ID" "$AWS_SECRET_ACCESS_KEY" > .env.s3
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
再利用できる標準 SDK クライアントを作成します。SDK にはリージョン文字列が必要で、R2 では auto を使用します。既存の設定を読み込むことで、CLI と SDK の操作対象を同じアカウントおよびバケットに揃えます。
cat > storage.mjs <<'JS'
import { S3Client } from "@aws-sdk/client-s3";
import { readFileSync } from "node:fs";
const config = JSON.parse(readFileSync("wrangler.jsonc", "utf8"));
export const Bucket = config.r2_buckets[0].bucket_name;
export const s3 = new S3Client({
region: "auto",
endpoint: `https://${config.account_id}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
});
JS
プレフィックスごとのライフサイクルルールを適用する
このステップでは、ライフサイクルポリシーを設定します。これは、オブジェクトの経過時間に応じて R2 が適用するストレージ操作の集合です。一時エクスポートは有効期限で削除し、保持するハンドブックはこれらのルールの対象外にします。ストレージクラスの移行は料金・アクセス区分を変更するものであり、オブジェクトを削除する操作ではありません。
この新しい一時バケットには、2 つのルールを設定します。temporary/ プレフィックスのオブジェクトは 2 日後に期限切れにし、そのプレフィックス配下の未完了アップロードは 1 日後に中止します。archive/ プレフィックスのオブジェクトは 30 日後に Infrequent Access へ移行します。retained/ を対象にするルールはありません。
API では経過時間を秒数で指定します。1 日は 86,400 秒です。JSON をそのまま保持できるよう、引用符付きのヒアドキュメントでポリシーを作成します。
cat > lifecycle.json <<'JSON'
{
"rules": [
{
"id": "temporary-exports",
"enabled": true,
"conditions": {
"prefix": "temporary/"
},
"deleteObjectsTransition": {
"condition": {
"type": "Age",
"maxAge": 172800
}
},
"abortMultipartUploadsTransition": {
"condition": {
"type": "Age",
"maxAge": 86400
}
}
},
{
"id": "archive-transition",
"enabled": true,
"conditions": {
"prefix": "archive/"
},
"storageClassTransitions": [
{
"condition": {
"type": "Age",
"maxAge": 2592000
},
"storageClass": "InfrequentAccess"
}
]
}
]
}
JSON
npx wrangler r2 bucket lifecycle set "$BUCKET" --file lifecycle.json --env-file=.env.management
npx wrangler r2 bucket lifecycle list "$BUCKET" --env-file=.env.management
set コマンドはポリシー全体を置き換えるため、この新しい実験用バケットだけを対象にしていることを確認します。正確に 2 つのプレフィックス、enabled 状態、経過時間が設定されていることを確認してください。既存のアプリケーション用バケットにこの置き換えを適用しないでください。Dashboard で同じバケットの Settings → Object Lifecycle Rules を開き、ルールの操作内容を確認します。内容は変更しないでください。
料金に関する注意: Infrequent Access には取得料金と最低保存期間があります。この実験では将来の移行を設定し、新しく作成した Standard オブジェクトをクリーンアップ時に削除します。30 日間待つことも、移行を強制することも、移行が実際に発生したと主張することもありません。
実際の Dashboard 画面には、今後実行される設定が表示されています。temporary/ のオブジェクトを 2 日後に削除し、同プレフィックスの未完了アップロードを 1 日後に中止し、archive/ のオブジェクトを 30 日後に Infrequent Access に移行します。この画像は待機期間の経過や実行完了を示すものではありません。次のステップで現在のメタデータを確認します。retained/ は両プレフィックスの対象外です。

新しく適用された有効期限メタデータを確認する
このステップでは、ポリシーを適用した後に新しいオブジェクトをアップロードします。R2 のドキュメントによると、新しいオブジェクトには適用可能な有効期限が x-amz-expiration に反映されます。変更されたルールが既存のオブジェクトに反映されるまでには、より長い時間がかかる場合があります。SDK では、このヘッダーを Expiration として取得できます。
cat > seed.mjs <<'JS'
import { PutObjectCommand, HeadObjectCommand } from "@aws-sdk/client-s3";
import { readFileSync } from "node:fs";
import { s3, Bucket } from "./storage.mjs";
for (const Key of ["temporary/export.txt", "archive/export.txt"]) {
await s3.send(new PutObjectCommand({ Bucket, Key, Body: readFileSync("document.txt"), ContentType: "text/plain" }));
}
await s3.send(new PutObjectCommand({ Bucket, Key: "retained/handbook.txt", Body: readFileSync("retained.txt"), ContentType: "text/plain" }));
for (const Key of ["temporary/export.txt", "archive/export.txt", "retained/handbook.txt"]) {
const head = await s3.send(new HeadObjectCommand({ Bucket, Key }));
console.log({ key: Key, expiration: head.Expiration || "none", storageClass: head.StorageClass || "STANDARD" });
}
JS
node --env-file=.env.s3 seed.mjs
temporary/export.txt に有効期限が表示され、保持対象のハンドブックには削除用の有効期限が表示されず、新しいアーカイブオブジェクトのストレージクラスが Standard であることを確認します。将来の移行は、現在の IA ストレージクラスではなく、リモートのルールによって確認します。新しいオブジェクトに期待される有効期限メタデータがない場合は、適用されたプレフィックスとポリシーを確認してください。それを有効期限チェックの成功とは見なさないでください。
保持対象のオブジェクトをダウンロードし、元のバイト列と比較します。
npx wrangler r2 object get "$BUCKET/retained/handbook.txt" --remote --file retained-download.txt --env-file=.env.management
cmp retained.txt retained-download.txt
ポリシーの読み取りに成功し、保持対象のオブジェクトを読み取れることによって、この実験の範囲内の結果を確認できます。実際のライフサイクルによる削除は非同期で、指定された有効期限の後に発生する場合があります。この実験では、数時間後に発生するイベントは評価しません。
実験用ストレージを明示的に空にする
このステップでは、将来のライフサイクル操作に依存せず、3 つのテスト用オブジェクトを今すぐ削除します。この実験では未完了アップロードを作成していませんが、バケットに未完了のパートがないことはオブジェクト一覧だけでは確認できないため、未完了アップロードの一覧も取得します。
cat > empty.mjs <<'JS'
import { DeleteObjectCommand, ListObjectsV2Command, ListMultipartUploadsCommand } from "@aws-sdk/client-s3";
import { s3, Bucket } from "./storage.mjs";
for (const Key of ["temporary/export.txt", "archive/export.txt", "retained/handbook.txt"]) await s3.send(new DeleteObjectCommand({ Bucket, Key }));
const objects = await s3.send(new ListObjectsV2Command({ Bucket }));
const uploads = await s3.send(new ListMultipartUploadsCommand({ Bucket }));
console.log("Objects:", objects.Contents || []);
console.log("Incomplete uploads:", uploads.Uploads || []);
JS
node --env-file=.env.s3 empty.mjs
オブジェクト配列と未完了アップロード配列が空であることを確認します。実験中にマルチパートセッションを作成した場合は、前の実験で使用した中止操作を、実際に所有している正確なキーとアップロード ID に対して実行し、その後でこれらの読み取り専用一覧をもう一度取得します。失敗した一覧取得を黙って無視しないでください。
空になったバケットとポリシーを削除する
このステップでは、オブジェクトとアップロードの確認に成功した後、所有しているバケットを削除します。ポリシーはバケット設定であり、バケットと一緒に消えます。
npx wrangler r2 bucket delete "$BUCKET" --env-file=.env.management
npx wrangler r2 bucket list --env-file=.env.management
生成された正確な名前が一覧から消えていることを確認します。認証済みの一覧取得が成功し、その名前が表示されないことを確認してください。管理用認証情報を無効化する前に、プラットフォーム上での削除確認を実行します。
実験用認証情報を無効化してログアウトする
このステップでは、この実験で残ったアクセスを閉じます。R2 API Tokens ページで、この実験用に作成した名前のオブジェクトトークンだけを無効化します。プロフィールの API Tokens ページで、この実験用に作成した別の R2 管理トークンを無効化します。バケットを削除してもトークンは無効化されません。また、Wrangler からログアウトしても S3 認証情報は無効化されません。
無効化した後、ローカルの認証情報ファイルを削除し、この VM からログアウトします。
rm .env.s3 .env.management
npx wrangler logout
構造化された ID 情報を確認します。ログアウト後は終了ステータスが 0 以外になることが想定されます。
npx wrangler whoami --json || true
loggedIn: false になっていることを確認します。通常の Dashboard ログインは保持してください。プラットフォームでは、ローカルの認証情報の削除と Wrangler のログアウトを確認します。2 つのトークンの無効化は、この実験では Dashboard で手動確認するチェックポイントです。ファイルの削除だけで無効化されたとは判断しません。
まとめ
対象範囲を限定した有効期限、将来のストレージクラス移行、マルチパートアップロードのクリーンアップルールを適用し、現在のメタデータを確認して、保持対象のデータを残したうえで明示的にクリーンアップしました。



