本文書はCloudflareのR2を利用してデータを保存していと思っているが利用するためにはクレジッドカードの情報を入力する必要があるため戸惑っている人、R2へのファイルのアップロードなど基本的な利用方法を知りたいといった入門者向でR2の説明よりも実際に手を動かして理解を深めたい人向けの内容になっています。

R2を利用するためにはクレジットカードの入力が必要だということを認識の上読み進めてください。

R2を利用するためのアカウントの作成からWrangler CLI, Cloudflare Workers、AWS CLI, aws-sdk-js-v3ライブラリによる ファイルのアップロード、ファイル一覧の取得など R2 の基本操作について説明を行っています。

Cloudflare R2はAmazon Web Services(AWS)のクラウドストレージであるS3のAWS CLI、aws-sdk-js-v3ライブラリを利用して操作することができます。

動作確認は macOS を利用して行っています。

Cloudflare R2とは

Cloudflare R2はAWS S3互換クラウドベースのオブジェクトストレージサービスです。写真や音声、動画などを保存することができます。1ヶ月10GBまでは無料ですることができ、読み込み書き込みの制限はありますがエグレス料金(ダウンロードする際の下りの通信量)が無料であるため非常に安価に利用することができます。

Cloudflare のアカウントの作成

R2 を利用するために Cloudflare のアカウントを作成する必要があります。アカウントを作成するために R2 の製品ページにブラウザからアクセスします。Cloudflare のページを日本語で表示させたい場合は右上にある地球儀のアイコンをクリックして言語を変更してください。

Cloudflare R2画面
Cloudflare R2画面

画面の中央にある”Sign up”ボタンをクリックします。アカウントを作成するためにメールアドレスとパスワードを入力する必要があります。

Sign Up画面
Sign Up画面

メールアドレスとパスワードを入力すると入力したメールアドレス宛てにアカウントを認証するためのメールが送信されます。

認証メールの送信
認証メールの送信

受信したメールにはリンクが含まれているのでリンクをクリックします。

認証メールの内容
認証メールの内容

クリックを行いサインアップが完了するとCloudflareのダッシュボードの Home ページが表示されます。アカウントの作成はこれで完了です。Cloudflare WorkersやCloudflare Pagesであればこの状態で利用することができます。

ダッシュボード画面
ダッシュボード画面

R2 の Activate(支払い情報入力)

R2 を利用するために左側のサイドメニューからR2を選択します。R2はサインアップするだけで利用できるわけではなく利用するためには支払い情報の登録を行う必要があります。

10GB の利用枠などもあるため支払い情報を入れたからといってすぐに支払いが開始されるわけではありません。

支払い情報を入力してもいいという人は”Activate R2”ボタンをクリックしてください。

Activate R2
Activate R2

料金の画面が表示されます。

料金画面
料金画面

無料枠は以下の通りです。無料枠を超えて利用すると支払いが発生します。

  • 1,000,000 Class A operatios
  • 10,000,000 Class B operations
  • 10GB Storage

Class A operatoins に主に書き込みなどの処理でListBuckets, PutBucket, ListObjects, PutObject, CopyObject, CompleteMultipartUpload, CreateMultipartUpload, ListMultipartUploads, UploadPart, UploadPartCopy and PutBucketEncryptionの処理が含まれます。

Class B operations は主に読み込みなどの処理でHeadBucket, HeadObject, GetObject, UsageSummary, GetBucketEncryption and GetBucketLocationの処理が含まれます。

無料の Operations には Free operations include DeleteObject, DeleteBucket and AbortMultipartUpload の処理が含まれます。

問題がない場合は”Proceed to Payment Details”ボタンをクリックします。クレジッドカード情報を入力する画面が表示されるので情報を入力して”Save Payment Information”ボタンをクリックしてください。

支払い情報入力画面
支払い情報入力画面

入力した支払い情報に問題がなければ購入完了画面が表示されます。

R2の設定

Bucket の作成

R2 が利用可能になると Overview 画面が表示されます。R2 では Bucket を作成してファイルをオブジェクトとして Bucket の中に保存していきます。ダッシュボードから Bucket を作成することができるので”Create bucket”ボタンをクリックします。

Overview画面
Overview画面
Bucket はオブジェクト(ファイル)を保存する入れ物です。ファイルシステムのようにディレクトリ(フォルダ)構造を持っておらずオブジェクト毎に割り当てるキーによってオブジェクトを識別します。キーに”/“を入れること(test/test.csv)で test ディレクトリの下に test.csv が保存されているように管理することができます。しかし実際にはディレクトリは存在しません。

動作確認のために利用する Bucket の名前にここでは”reffect”という名前をつけています。任意の名前をつけることができるので好きな名前をつけてください。Automaticを選択すると保存する場所が自動で決められます。近くの場所が選ばれるようになっています。

Bucketの名前設定
Bucketの名前設定

作成が完了すると以下の画面が表示されます。この画面からBucketに保存されているオブジェクトを確認することができます。作成したばかりなので画像は存在しないため Bucket の中身は空です。

Bucketの内容の確認画面
Bucketの内容の確認画面

ファイルのアップロード

ダッシュボードからファイルのアップロードを行うことができます。”Your bucket is ready. Add files to get started.”をクリックすることで OS 上に保存されているファイルを選択することができます。Drag&Drop でもアップロードを行うことができます。

試しに Drag&Drop で”cloudflare-r2-1.png”ファイルをアップロードします。アップロードが完了するとオブジェクトストレージということでObjects列の下にアップロードしたファイル名が表示されます。

アップロードしたファイル
アップロードしたファイル

アップロードしたファイルは右側にある”…”ボタンをクリックすることでダウンロードすることもできます。必要であればアップロードした画像をダウンロードしてみてください。

ファイル名をクリックするとファイルの詳細情報が表示されます。

ファイルの詳細画面
ファイルの詳細画面

ファイルの公開

ファイルをインターネット上に公開するためには公開設定が必要となります。公開設定にはドメインを利用する方法とR2.devのサブドメインを利用する方法があります。ドメインの設定を行なっていない場合にはR2.dev subdomainを利用します。

Bucket 毎に設定を行うのでBucketの詳細画面で”Settings”タブをクリックします。下記の画面が表示されるので赤丸の”Allow Access”ボタンをクリックします。

公開設定画面
公開設定画面
公開設定はデフォルトでは行われていないため Bucketの名前の下のPublic URL Accessが”Not allowed”になっています。設定が完了すると”Allowed”に変わります。

Public アクセスを許可するかどうか確認の画面が表示されるので”allow”と入力して”Allow”ボタンをクリックします。

公開設定確認画面
公開設定確認画面

設定が完了すると Public R2.dev Bucket URL が表示されます。また公開を停止したい場合には”Disallow Access”ボタンから行うことができます。

URLの確認
URLの確認

設定後オブジェクトの詳細画面に行くと Public Bucket が表示され表示されている URL にアクセスするとアップロードした画像が表示されます。

ファイルのPublic URLの確認
ファイルのPublic URLの確認

ダッシュボードからのファイルのアップロードとインターネット上からのファイルのアクセス方法を確認することができました。

Wranglerコマンドによる操作

ダッシュボードからファイルのアップロードとアップロードしたファイルを確認しましたがR2に保存されているファイルを操作するためにはさまざまな方法があります。最初にWranglerコマンドを利用して操作を行います。

プロジェクトの作成

WranglerコマンドはR2に限定されたものではなくCloudflare上のサービスを操作する際に利用することができます。Wranglerコマンドを利用することでBucketの作成やファイルのアップロードを行うことができます。

Wranglerコマンドを利用するためにOS上で実行するパッケージをインストールする必要があります。wranglerは”npm install -g wrangler”でグローバルにインストールすることもできますが Cloudflare Workers のプロジェクトを”npm create cloudflare@latest”で作成するとwrangler パッケージ もインストールされるためこちらの方法を利用します。

作成したプロジェクトは後ほど Workers を利用したファイルアップロードで利用します。

“npm create cloudflare”コマンドを実行するとプロジェクト名、TypeScript の利用、テンプレートの選択、Deploy について質問されます。プロジェクト名はここでは cloudflare-r2-test としていますが任意の名前をつけてください。アプリケションのタイプは”Hello World” Worker, TypeScript は”Yes”, Deploy は”No”を選択しています。


 % npm create cloudflare@latest

using create-cloudflare version 2.8.4

╭ Create an application with Cloudflare Step 1 of 3
│ 
╰ In which directory do you want to create your application? also used as applic├ In which directory do you want to create your application?
│ dir ./cloudflare-r2-test
│
├ What type of application do you want to create?
│ type "Hello World" Worker
│
├ Do you want to use TypeScript?
│ yes typescript
│
├ Copying files from "hello-world" template
│ 
├ Retrieving current workerd compatibility date 
│ compatibility date 2023-12-18
│ 
├ Do you want to use git for version control?
│ yes git
│
╰ Application created 

╭ Installing dependencies Step 2 of 3
│ 
├ Installing dependencies 
│ installed via `npm install`
│ 
├ Installing @cloudflare/workers-types 
│ installed via npm
│ 
├ Adding latest types to `tsconfig.json` 
│ added @cloudflare/workers-types/2023-07-01
│ 
├ Committing new files 
│ git commit
│ 
╰ Dependencies Installed 

╭ Deploy with Cloudflare Step 3 of 3
│ 
├ Do you want to deploy your application?
│ no deploy via `npm run deploy`
│
├  APPLICATION CREATED  Deploy your application with npm run deploy
│ 
│ Navigate to the new directory cd cloudflare-r2-test
│ Run the development server npm run start
│ Deploy your application npm run deploy
│ Read the documentation https://developers.cloudflare.com/workers
│ Stuck? Join us at https://discord.gg/cloudflaredev
│ 
╰ See you again soon! 

プロジェクトの作成が完了するとプロジェクト名のフォルダが作成されるので移動してインストールした wrangler のバージョンを確認しておきます。


 % cd cloudflare-r2-test
 % npx wrangler --version
 ⛅️ wrangler 3.22.1
-------------------

package.json の中身も確認しておきます。


{
  "name": "cloudflare-r2-test",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "deploy": "wrangler deploy",
    "dev": "wrangler dev",
    "start": "wrangler dev"
  },
  "devDependencies": {
    "@cloudflare/workers-types": "^4.20231218.0",
    "typescript": "^5.0.4",
    "wrangler": "^3.0.0"
  }
}

ログイン

wrangler コマンドを利用してR2を操作するためにCloudflareへのログインが必要になります。ログインは”npx wrangler login”コマンドで行います。


 % npx wrangler login
 ⛅️ wrangler 3.22.1
------------------
Attempting to login via OAuth...
Opening a link in your default browser: https://dash.cloudflare.com/oauth2/auth?response_type=code&client_id=54d11594-84e4-41aa-b438-e81b8fa78ee7&redirect_uri=http%3A%2F%2Flocalhost%3A8976%2Foauth%2Fcallback&scope=account%3Aread%20user%3Aread%20workers%3Awrite%20workers_kv%3Awrite%20workers_routes%3Awrite%20workers_scripts%3Awrite%20workers_tail%3Aread%20d1%3Awrite%20pages%3Awrite%20zone%3Aread%20ssl_certs%3Awrite%20constellation%3Awrite%20offline_access&state=girJ-rC~Yvm8~pEmzB0xDrpj~RMiHu-F&code_challenge=a3vUQIutP83UMUvbezxPabf2mjIG3ClSzvZ_OP_CpgI&code_challenge_method=S256
Successfully logged in.

自動でブラウザが起動してダッシュボードにログインしている場合には”Allow Wrangler to make changes to your Cloudflare account?”の画面が表示されるので”Allow”を選択します。ダッシュボードにログインしていない場合には ID とパスワードを聞かれるので入力してください。ログインが完了すると画面には”You have granted authorization to Wrangler!”が表示されます。これで wrangler コマンドを利用してクラウド上のサービスを操作できるようになります。

認証画面
認証画面

ログインに成功するとwranglerコマンドでR2を操作することができます。

“npx wrangler whoami”コマンドを利用することでログインしているユーザを確認することができます。またログアウトしたい場合には”npx wrangler logout”でログアウトできます。1

ファイルのアップロード

プロジェクトフォルダ直下にcloudflare-r2-2.pngファイルを保存してwranglerコマンドを利用してファイルのアップロードを行います。


 % npx wrangler r2 object put reffect/cloudflare-r2-2.png --file=cloudflare-r2-2.png
 ⛅️ wrangler 3.22.1
------------------
Creating object "cloudflare-r2-2.png" in bucket "reffect".
Upload complete.

ダッシュボードで確認するとファイルのアップロードが正常に行われていることが確認できます。

アップロードの確認
アップロードの確認

ファイルのダウンロード

wrangler コマンドを利用して bucket に保存されているファイルをローカルにダウンロードすることもできます。reffect Bucketに保存済みのcloudflare-r2-1.pngファイルをダウンロードしています。


 % npx wrangler r2 object get reffect/cloudflare-r2-1.png
 ⛅️ wrangler 3.22.1
------------------
Downloading "cloudflare-r2-1.png" from "reffect".
Download complete.

ファイルの削除

ファイルの削除も wrangler コマンドを利用して行うことができます。


 % npx wrangler r2 object delete reffect/cloudflare-r2-2.png
  ⛅️ wrangler 3.22.1
------------------
Deleting object "cloudflare-r2-2.png" from bucket "reffect".
Delete complete.

wrangler コマンドを利用したR2の操作方法を理解することができました。

AWS CLI による操作

AWS CLIはAWSをコマンドラインで操作するためのAWS 用のツールですがR2でも利用することができます。AWS CLIは、Windows, macOS, LinuxのOS向けに提供されており利用するためには OS にインストールを行う必要があります。

AWS CLI のインストール

本文書では macOS を利用しているので HomeBrew を利用して AWS CLI のインストールを行います。

インストールする awscli の情報を brew info コマンドを利用して確認します。バージョンは 2.11.27 で Not installed と表示されているのでインストールが行われていないことが確認できます。


 % brew info awscli
==> awscli: stable 2.15.0 (bottled), HEAD
Official Amazon AWS command-line interface
https://aws.amazon.com/cli/
Not installed
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/awscli.rb
License: Apache-2.0
==> Dependencies
Build: cmake ✘, pkg-config ✘, rust ✘
Required: cffi ✘, docutils ✘, openssl@1.1 ✔, pycparser ✘, python@3.11 ✘, six ✘
==> Options
--HEAD
	Install HEAD version
==> Caveats
The "examples" directory has been installed to:
  /usr/local/share/awscli/examples
==> Analytics
install: 173,438 (30 days), 302,393 (90 days), 337,869 (365 days)
install-on-request: 169,819 (30 days), 298,774 (90 days), 334,250 (365 days)
build-error: 0 (30 days)

インストールはbrew installコマンドで行います。


% brew install awscli

インストールが完了後—versionオプションでバージョンを確認することができます。


% aws --version
aws-cli/2.15.6 Python/3.11.6 Darwin/22.6.0 source/x86_64 prompt/off

API Token の作成

AWS CLI を利用してR2を操作するためにAPI Tokenが必要となります。API TokenはCloudflareのダッシュボードから作成することができます。

ダッシュボードのR2のOverview画面の右上にある”Manage R2 API Tokens”をクリックします。

Cloudflare Dashboard
Cloudflare Dashboard

API Tokensの画面が表示されるので”Create API token”ボタンをクリックします。

API Token画面
API Token画面

Bucketのオブジェクトの作成などが行えるように”Admin Read & Write”か”Object Read & Write”を選択して”Create API Token”ボタンをクリックします。

Permissionsの設定
Permissionsの設定

作成が完了すると表示される”Access Key ID”と”Secret Access Key”が必要となります。この情報は他の人に見せないように大切に保管してください。また後でキーを確認することができないのでKeyの情報は確実に控えておいてくだい。

API Token の設定

API Token の作成が完了したら”aws configure”コマンドを実行して設定を行います。コマンドを実行すると下記のように”Access Key ID”などを聞かれるので各自が作成したAPI Tokenの値を設定してください。region name は”auto”, output format は”json”を設定します。


% aws configure
AWS Access Key ID [None]: 2627242a9c1bf30695b2c1241cab8ac9
AWS Secret Access Key [None]: 8d893715f1410b681ce31e5fe0eab2159c018d45f355924ca932a73c861c5796
Default region name [None]: auto
Default output format [None]: json
上記のKeyをそのまま利用しても動作しません。

設定した内容はユーザのホームディレクトリ直下の.aws フォルダの中に config, credentials ファイルとして保存されます。

AWS のツールを利用してR2を操作する場合は”https://<ACCOUNT_ID>.r2.cloudflarestorage.com”のエンドポイントに対して実行します。ACCOUNT_ID は各自によって異なる値になり, R2のOverview画面の右側に表示されています。

ここから<\ACCOUNT_ID>と表示されている場所には各自の ACCOUNT_ID を設定してください。

Bucket の確認

aws コマンドを利用してBucketの一覧を表示させます。コマンドを実行すると Bucket の reffect を確認することができます。もしToken作成時に”Admin Read & Write”を選択していない場合には”An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied”のメッセージが表示されます。


 % aws s3api list-buckets --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com
{
    "Buckets": [
        {
            "Name": "reffect",
            "CreationDate": "2023-07-03T01:30:16.236000+00:00"
        }
    ],
    "Owner": {
        "DisplayName": "a51248e16eb1cfe6a9a262e7XXXXX",
        "ID": "a51248e16eb1cfe6a9a262e7XXXXX"
    }
}

“aws s3″コマンドでも可能です。


 % aws s3  --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com ls
2023-07-03 10:30:16 reffect

Bucket内のオブジェクトを確認する場合は”aws s3api list-objects”コマンドを利用します。ダッシュボードでアップロードした cloudflare-r2-1.png の情報が表示されます。


 % aws s3api list-objects --bucket reffect --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com
{
    "Contents": [
        {
            "Key": "cloudflare-r2-1.png",
            "LastModified": "2023-07-05T05:14:19.516000+00:00",
            "ETag": "\"71c67fbd5fa5510ef2e5a576d22236e8\"",
            "Size": 856183,
            "StorageClass": "STANDARD",
            "Owner": {
                "DisplayName": "a51248e16eb1cfe6a9a262e7XXXXX",
                "ID": "a51248e16eb1cfe6a9a262e7XXXXX"
            }
        }
    ],
    "RequestCharged": null
}

ファイルのアップロード

AWS CLI コマンドを利用してローカルに保存している cloudflare-r2-2.png ファイルをアップロードします。


% aws s3api put-object --endpoint-url https://<ACCOUNT_ID>.r2.cloudflarestorage.com --bucket reffect --key cloudflare-r2-2.png  --body cloudflare-r2-2.png
{
    "ETag": "\"3c76af5c7b73361aaa4b0e90b4e5f170\"",
    "VersionId": "7e76db1ca2a73bc83af43377c7052259"
}

“AWS CLI”を利用することで”R2″の操作が行えることが確認できました。

@aws-sdk/client-s3を利用した場合

ここまでは主にコマンドを利用してR2の操作を行ってきましたがここでは@aws-sdk/client-s3ライブラリを利用してR2の操作を行います。

プロジェクトの作成

プロジェクトフォルダ”aws-sdk-r2”を作成します。作成したプロジェクトフォルダで@aws-sdk/client-s3 をインストールして利用します。


% mkdir aws-sdk-r2
% cd aws-sdk-r2

“npm init -y”コマンドを実行してpackage.jsonファイルを作成します。


 % npm init -y
Wrote to /Users/mac/Desktop/aws-sdk-r2/package.json:

{
  "name": "aws-sdk-r2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

@aws-sdk/client-s3 のインストール

@aws-sdk/client-s3 のインストールを行います。


% npm install @aws-sdk/client-s3

@aws-sdk/client-s3 を利用する際も API Token を利用するため dotenv ライブラリのインストールを行い環境変数を利用して設定します。


% npm install dotenv

import 文が利用できるように package.json ファイルに”type”:“module”を追加します。


{
  "name": "aws-sdk-r2",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@aws-sdk/client-s3": "^3.484.0",
    "dotenv": "^16.3.1"
  },
  "type": "module"
}

.env ファイルを作成して “ACCESS KEY ID” と “SECRET ACCESS KEY” の設定を行います。ENDPOINT の設定も.env ファイルに設定しておきます。


R2_ACCESS_KEY_ID=2627242a9c1bf30695b2c1241cab8ac9
R2_SECRET_ACCESS_KEY=8d893715f1410b681ce31e5fe0eab2159c018d45f355924ca932a73c861c5796
ENDPOINT=https://a51248e16eb1cfe6a9a262e7XXXXX.r2.cloudflarestorage.com
上記のKeyをそのまま利用しても動作しません。各自が取得したKeyを設定してください。ENDPOINTの ACCOUNT_ID(a51248e16eb1cfe6a9a262e7XXXXX) も各自の環境での値を設定してください。

Bucket の情報取得

index.js ファイルを作成しコードを記述します。@aws-sdk/client-s3 から import した S3Client を利用して S3 インスタンスを作成します。インスタンスを作成する際に”ACCESS KEY ID” と “SECRET ACCESS KEY”を指定します。ListBucketsCommand で R2 に作成されいてる Buskets の情報を取得し、ListObjectsV2Command で Bucket の reffect に保存されているオブジェクトの一覧を取得しています。Token作成時に”Admin Read & Write”を選択していない場合はListBucketsCommandの行は削除してください。


import {
  S3Client,
  ListBucketsCommand,
  ListObjectsV2Command,
} from '@aws-sdk/client-s3';
import dotenv from 'dotenv';

dotenv.config();

const S3 = new S3Client({
  region: 'auto',
  endpoint: process.env.ENDPOINT,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
  },
});

console.log(await S3.send(new ListBucketsCommand('')));

console.log(await S3.send(new ListObjectsV2Command({ Bucket: 'reffect' })));

index.js ファイルの作成ができたら、node コマンドを利用して index.js ファイルを実行します。Bucket の情報と Bucket 内のオブジェクトの情報が表示されていることが確認できます。


 % node index.js
{
  '$metadata': {
    httpStatusCode: 200,
    requestId: undefined,
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  Buckets: [ { Name: 'reffect', CreationDate: 2023-07-03T01:30:16.236Z } ],
  Owner: {
    DisplayName: 'a51248e16eb1cfe6a9a262e7XXXXX',
    ID: 'a51248e16eb1cfe6a9a262e7XXXXX'
  }
}
{
  '$metadata': {
    httpStatusCode: 200,
    requestId: undefined,
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  Contents: [
    {
      Key: 'cloudflare-r2-1.png',
      LastModified: 2023-07-05T05:14:19.516Z,
      ETag: '"71c67fbd5fa5510ef2e5a576d22236e8"',
      Size: 856183,
      StorageClass: 'STANDARD'
    },
    {
      Key: 'cloudflare-r2-2.png',
      LastModified: 2023-07-05T07:10:28.139Z,
      ETag: '"3c76af5c7b73361aaa4b0e90b4e5f170"',
      Size: 339875,
      StorageClass: 'STANDARD'
    }
  ],
  IsTruncated: false,
  KeyCount: 2,
  MaxKeys: 1000,
  Name: 'reffect'
}

ファイルのアップロード

ローカルに保存した cloudflare-r2-3.png ファイルを@aws-sdk/client-s3 を利用してアップロードする方法を確認します。アップロードにはPubObjectCommandを利用します。ファイルアップロード後に Bucket に保存されているオブジェクトをListObjectV2Commandを利用して取得しています。fs.createReadStreamメソッドを利用して保存したファイルを指定しています。


import fs from 'fs';
import {
  S3Client,
  PutObjectCommand,
  ListObjectsV2Command,
} from '@aws-sdk/client-s3';
import dotenv from 'dotenv';

dotenv.config();

const S3 = new S3Client({
  region: 'auto',
  endpoint: process.env.ENDPOINT,
  credentials: {
    accessKeyId: process.env.R2_ACCESS_KEY_ID,
    secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
  },
});

await S3.send(
  new PutObjectCommand({
    Body: fs.createReadStream('cloudflare-r2-3.png'),
    Bucket: 'reffect',
    Key: 'cloudflare-r2-3.png',
  })
);

console.log(await S3.send(new ListObjectsV2Command({ Bucket: 'reffect' })));

node index.js コマンドを実行します。ListObjectsV2Command の結果によりアップロードしたファイルが追加されていることが確認できます。


% node index.js
{
  '$metadata': {
    httpStatusCode: 200,
    requestId: undefined,
    extendedRequestId: undefined,
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
  Contents: [
    {
      Key: 'cloudflare-r2-1.png',
      LastModified: 2023-07-05T05:14:19.516Z,
      ETag: '"71c67fbd5fa5510ef2e5a576d22236e8"',
      Size: 856183,
      StorageClass: 'STANDARD'
    },
    {
      Key: 'cloudflare-r2-2.png',
      LastModified: 2023-07-05T07:10:28.139Z,
      ETag: '"3c76af5c7b73361aaa4b0e90b4e5f170"',
      Size: 339875,
      StorageClass: 'STANDARD'
    },
    {
      Key: 'cloudflare-r2-3.png',
      LastModified: 2023-07-05T09:55:09.850Z,
      ETag: '"0e72511b51cd30247221fdf3d31d6245"',
      Size: 324244,
      StorageClass: 'STANDARD'
    }
  ],
  IsTruncated: false,
  KeyCount: 3,
  MaxKeys: 1000,
  Name: 'reffect'
}

@aws-sdk/client-s3 を利用したファイルのアップロードと Bucket に保存されているファイル一覧を確認することができました。

Cloudflare Workersによる設定

プロジェクトはwranglerコマンドを利用する際に作成した”cloudflare-r2-test”プロジェクトを使います。これまでの動作確認とは異なり入力フォームを利用してPOSTリクエストを利用します。POSTリクエストで送信したデータをWorkersが受け取り、WorkersからR2にファイルのアップロードを行います。

index.html ファイルの作成

プロジェクトフォルダの直下に index.html ファイルを作成して入力フォームを追加します。fetch 関数の URL に設定している”http://127.0.0.1:8787/“はWorkersが開発時に利用する開発サーバのURLです。後ほど実行すると”npx wrangler dev”のコマンド後に表示されるURLを記述してください。EventListenerでchangeイベントを設定しているのでinput要素でファイルを選択したらuploadImage関数が実行されます。

<!DOCTYPE html>
<html>
  <head>
    <title>Upload Images</title>
  </head>
  <body>
    <h1>File Upload</h1>
    <form>
      <label for="" image>File Select:</label>
      <input type="file" name="image" id="image" />
    </form>

    <script>
      async function uploadImage(file) {
        fetch('http://127.0.0.1:8787/', {
            method: 'POST',
            body: file,
        })
            .then((response) => response.text())
            .then((data) => console.log(data))
            .catch((error) => console.error(error));
        }

      const image = document.getElementById('image');
      image.addEventListener('change', (e) => {
          const formData = new FormData();
          console.log(e.target.files[0]);
          formData.append('file', e.target.files[0]);
          uploadImage(formData);
      });
    </script>
  </body>
</html>

Workers 側の設定

Workers の設定はsrcフォルダのindex.tsファイルで行います。WorkersからR2へのファイルのアップロードにはputメソッドを利用します。putメソッドの第一引数にはkeyを設定します。ここではkeyをファイル名としています。第二引数には value を設定しますが value は ReadableStream, ArrayBuffer, ArraryBufferView, string, null, Blog を指定することができます。ここでは受け取ったファイルを指定しています。第三引数にはオプションでファイルの Content-Type を指定しています。

export default {
export interface Env {
	MY_BUCKET: R2Bucket;
}

export default {
	async fetch(request: Request, env: Env) {
		const corsHeaders = {
			'Access-Control-Allow-Origin': '*',
		};

		const formData = await request.formData();
		const file = formData.get('file') as File;
		const fileName = file.name;
		const contentType = file.type;
		const fileBuffer = await file.arrayBuffer();

		try {
			await env.MY_BUCKET.put(fileName, fileBuffer, {
				httpMetadata: {
					contentType,
				},
			});
		} catch (err) {
			console.log(err);
		}

		return new Response('image uploaded successfully ', {
			headers: {
				...corsHeaders,
			},
		});
	},
};

env.MY_BUCKET で設定している環境変数は wrangler.toml ファイルで行います。wrangler.toml ファイルは Worker プロジェクトを作成時に自動で作成されており R2 Bucket についての情報も登録されていますがコメントされています。コメントを外し各環境にあった値に変更します。bucket_name はここでは reffect になります。


name = "cloudflare-r2-test"
main = "src/index.ts"
compatibility_date = "2023-12-18"
//略
# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files.
# Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "reffect"
preview_bucket_name = "reffect"

開発環境で Workersの動作確認を行う場合はpackage.jsonに登録されているscriptの”npm run start”コマンドを利用しますが開発環境でクラウド上のR2を利用する場合は”npx wrangler dev —remote”とオプションの”—remote”をつけて起動させます。

”npx wrangler dev —remote”を実行した際に wrangler.toml ファイルの中で preview_bucket_name を設定していない場合にはエラーが発生します。エラーでは本番用ではなく開発用の Bucket を用意して wrangler.toml ファイルの preview_bucket_name に設定するように言っています。ここでは動作確認で本番用ではないので同じ Bucket を利用しています。


% npx wrangler dev --remote
 ⛅️  wrangler 3.22.1
------------------

✘ [ERROR] In development, you should use a separate r2 bucket than the one you'd use in production. Please create a new r2 bucket with "wrangler r2 bucket create <name>" and add its name as preview_bucket_name to the r2_buckets "MY_BUCKET" in your wrangler.toml

ファイルのアップロードの動作確認

作成した index.html ファイルを開くと下記の画面が表示されます。

ファイルアップロード画面
ファイルアップロード画面

“npx wrangler dev —remote”コマンドで Workers の開発サーバを起動します。


 % npx wrangler dev --remote
 ⛅️ wrangler 3.22.1
------------------
Your worker has access to the following bindings:
- R2 Buckets:
  - MY_BUCKET: reffect
[wrangler:inf] Ready on http://localhost:63877
Total Upload: 1.77 KiB / gzip: 0.86 KiB
Total Upload: 5.21 KiB / gzip: 1.58 KiB

index.html画面からファイルを選択するとindex.htmlファイルで指定したfetch関数のurlに対してPOSTリクエストが送信されます。そのUrlに設定したPORTでWorkersがPOSTリクエストを受け取ります。

アップロードに成功した場合はブラウザのデベロッパーツールのコンソールに”image uploaded successfully”が表示され Cloudflare のダッシュボードを見るとアップロードしたファイルが Bucket の中に保存されているはずです。この後に Workers からアップロードしたファイルの確認も行います。

index.html ファイルから送信されてくる POST リクエストの Headers の情報を確認したい場合は以下のように行うことで確認ができます。


const { headers } = request;

let headersObject = Object.fromEntries(headers);
let requestHeaders = JSON.stringify(headersObject, null, 2);
console.log(`Request headers: ${requestHeaders}`);

今回のコードであれば”npx wrangler dev —remote”を実行したターミナルに headers 情報が表示されます。


Request headers: {
  "accept": "*/*",
  "accept-encoding": "gzip",
  "accept-language": "ja,en-US;q=0.9,en;q=0.8",
  "cf-connecting-ip": "123.223.41.4",
  "cf-ew-preview-server": "https://22m228.cfops.net",
  "cf-ipcountry": "JP",
  "cf-ray": "4e23b4c6afa1f8d7",
  "cf-visitor": "{\"scheme\":\"https\"}",
  "connection": "Keep-Alive",
  "content-length": "278582",
  "content-type": "multipart/form-data; boundary=----WebKitFormBoundarynmBfH7Q2Y72Di6MJ",
  "host": "cloudflare-r2-test.johndoe.workers.dev",
  "origin": "http://127.0.0.1:5500",
  "referer": "http://127.0.0.1:5500/",
  "sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
  "sec-ch-ua-mobile": "?0",
  "sec-ch-ua-platform": "\"macOS\"",
  "sec-fetch-dest": "empty",
  "sec-fetch-mode": "cors",
  "sec-fetch-site": "same-site",
  "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
  "x-forwarded-proto": "https",
  "x-real-ip": "123.223.41.4"
}

アップロードしたファイルの確認

ファイルを Bucket に保存したい場合には put メソッドを利用しましたが Bucket のオブジェクトの一覧を取得したい場合には list メソッドを利用することができます。ファイルアップロードに利用した worker.js ファイルを利用していますが処理は request.method の値を確認して分岐させています。POST リクエストの場合はアップロード処理、それ以外のリクエストはオブジェクトの一覧を json データで戻すようにしています。


export default {
  async fetch(request, env) {
    if (request.method === 'POST') {

      const corsHeaders = {
        'Access-Control-Allow-Origin': '*',
      };

      const formData = await request.formData();
      const fileName = formData.get('file').name;
      const contentType = formData.get('file').type;
      const fileBuffer = await formData.get('file').arrayBuffer();

      try {
        await env.MY_BUCKET.put(fileName, fileBuffer, {
          httpMetadata: {
            contentType,
          },
        });
      } catch (err) {
        console.log(err);
      }

      return new Response('image uploaded successfully ', {
        headers: {
          ...corsHeaders,
        },
      });
    } else {
      const objectLists = await env.MY_BUCKET.list();
      const json = JSON.stringify(objectLists, null, 2);

      return new Response(json, {
        headers: {
          'content-type': 'application/json;charset=UTF-8',
        },
      });
    }
  },
};

GETリクエストはブラウザから行うことができるのでURLにWorkers の開発サーバの URL(http://127.0.0.1:63877)を設定すると Bucket に保存されているオブジェクトの一覧が表示されます。


{
  "objects": [
    {
      "uploaded": "2023-07-05T05:14:19.516Z",
      "checksums": {
        "md5": "71c67fbd5fa5510ef2e5a576d22236e8"
      },
      "httpEtag": "\"71c67fbd5fa5510ef2e5a576d22236e8\"",
      "etag": "71c67fbd5fa5510ef2e5a576d22236e8",
      "size": 856183,
      "version": "7e76db86f8148115dde4b12ffae682e2",
      "key": "cloudflare-r2-1.png"
    },
//略
    {
      "uploaded": "2023-07-05T10:05:09.850Z",
      "checksums": {
        "md5": "f4eb01a95e3e071361e3772e1fa895f1"
      },
      "httpEtag": "\"f4eb01a95e3e071361e3772e1fa895f1\"",
      "etag": "f4eb01a95e3e071361e3772e1fa895f1",
      "size": 219896,
      "version": "7e76d77dee49a653b2b6553ff4ab3679",
      "key": "cloudflare-r2-4.png"
    }
  ],
  "truncated": false,
  "delimitedPrefixes": []
}
WorkersにアクセスするためのPortは環境によって異なります。

Workersを利用してもR2の操作が行えることが確認できました。

WorkdersのDeploy

作成したWokersは”npx wrangler deploy”コマンドで実行することができます。


 %  npx wrangler deploy      
 ⛅️ wrangler 3.22.1
-------------------
Your worker has access to the following bindings:
- R2 Buckets:
  - MY_BUCKET: reffect
Total Upload: 1.03 KiB / gzip: 0.49 KiB
Uploaded cloudflare-r2-test (1.37 sec)
Published cloudflare-r2-test (3.82 sec)
  https://cloudflare-r2-test.reffect_dev.workers.dev
Current Deployment ID: 4a507d4d-9081-497d-b96e-e4b9d7fb9750

Deployが完了したらindex.htmlのfetch関数のURLを表示されているURLに変更してください。今後はネット上に公開されたWorkersを利用してR2へのファイルの保存を行うことができます。

ここまでの動作確認でR2にファイルをアップロードするための方法をいくつも学ぶことができたのでCloudFlareのR2について基本的な知識もかなり身についたのではないでしょうか。