【R2の通知機能<Event Notifications>】Cloudflare Queues/Workers連携による自動化の検証

はじめに
下記公式ブログで発表内容をまとめていただいております。
Cloudflare Builder Day 2024 ブログ:https://blog.cloudflare.com/builder-day-2024-announcements/#event-notifications-for-r2-is-now-ga
R2はAWSでいうS3のような機能になっており、コンテンツの保存やWEBコンテンツの配信などで使用できます。
今回の機能はR2にファイルをアップロードや削除を行った際にイベントを発火して、CloudflareのQueuesやWorkersなどと連携して色々できそうです。
具体的にどのようなことができるのか、設定はどうしたらいいのか気になったので検証を行ってみました。
Cloudflare doc:
https://developers.cloudflare.com/r2/buckets/event-notifications/
R2 Event notificationsの動作検証
1:アップロードしたファイル情報を分析サーバに転送(デリート時にも適用可能)
2:ファイルがアップロードされたらslack通知
3:リサイズした画像をR2バケットに保存
公式ドキュメントにチュートリアルがありますので、先にそちらを試しておくと理解も早いかと思います。
公式チュートリアル:https://developers.cloudflare.com/r2/tutorials/upload-logs-event-notifications/
内容:アップロードしたファイル情報をR2 event notificationsを使用して別のバケットに保存
基本設定
wranglerでCloudflareにログイン実施
$ npx wrangler login
$ npx wrangler whoami
1:アップロードしたファイル情報を分析サーバに転送(デリート時にも適用可能)
R2ではアップロードされたファイルの容量、Aクラス操作、Bクラス操作の可視化はできますが、アップロードされた実際のファイルの数などは可視化できません。
event notificationsを使用すればファイルがアップロードされたら分析サーバに飛ばして可視化することができます。
※今回は分析サーバとしてopenobserveを使用します。
構成図

queue 作成
下記コマンドでqueuesを作成します。
$ npx wrangler queues create test-queue-r2-notifications-202501

R2 作成
下記コマンドでファイルをアップロードするR2を作成します。
$ npx wrangler r2 bucket create test-input-bucket-r2-notifications-202501

R2 bucketにevent notificationsを設定
該当のバケットにファイルが作成されたらevent notificationsが発火するように設定します。
ファイルが作成された場合や、削除された場合などの条件の指定方法については下記ドキュメントをご確認ください。
Cloudflare doc:https://developers.cloudflare.com/r2/buckets/event-notifications/#event-types
prefixなどの指定については下記ドキュメントをご確認ください。
※今回は特に指定はしません。
Cloudflare doc:https://developers.cloudflare.com/r2/buckets/event-notifications/#enable-event-notifications-via-wrangler
$ npx wrangler r2 bucket notification create test-input-bucket-r2-notifications-202501 --event-type object-create --queue test-queue-r2-notifications-202501
Cloudflareの画面から下記に移動します。
R2 オブジェクト ストレージ -> バケット名 -> 設定
下にスクロールするとイベント通知項目があり、そこに設定が入ったことを確認します。

分析サーバ用意
今回はdockerでopenobserveを使用してデータを受け取れるようにしておきます。
通信を暗号化したいので`/home/user01/o2/ssl/`配下にSSL証明書を配置してnginxを通してます。
compose.yml
※ユーザ名(EMAIL)とパスワードは指定してください
↓
services:
openobserve:
image: openobserve/openobserve:v0.14.2
ports:
- 5080:5080
restart: always
volumes:
- /home/user01/o2/data:/data
environment:
- ZO_DATA_DIR=/data
- ZO_ROOT_USER_EMAIL=ログインユーザ名
- ZO_ROOT_USER_PASSWORD=ログインパスワード
nginx:
image: nginx:alpine3.19
ports:
- "80:80"
- "443:443"
restart: always
volumes:
- /home/user01/o2/openobserve.conf:/etc/nginx/conf.d/openobserve.conf
- /home/user01/o2/ssl/:/etc/nginx/ssl/
depends_on:
- openobserve
openobserve.conf
※FQDNは指定してください
↓
server {
listen 80;
server_name FQDN;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name FQDN;
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
location / {
proxy_pass http://openobserve:5080;
}
}
openobserve起動
$ docker compose up -d

ログインできることを確認しておきます。

workersを作成
queuesから実行させるworkersを作成します。
$ npm create cloudflare@latest -- test-workers-r2-notifications-202501
選択する内容は下記を設定しました。
├ Which template would you like to use?
│ type Hello World Worker
│
├ Which language do you want to use?
│ lang TypeScript
…
├ Do you want to use git for version control?
│ no git
├ Do you want to deploy your application?
│ no deploy via `npm run deploy`
`src/index.ts`を設定します。
export interface Env {
OPENOBSERVE_USERNAME: string;
OPENOBSERVE_PASSWORD: string;
OPENOBSERVE_FQDN: string;
}
export default {
async queue(batch, env): Promise<void> {
const batchId = new Date().toISOString().replace(/[:.]/g, '-');
const fileName = `upload-logs-${batchId}.json`;
const fileContent = JSON.stringify(batch.messages);
// 環境変数から認証情報を取得
const username = env.OPENOBSERVE_USERNAME;
const password = env.OPENOBSERVE_PASSWORD;
// Basic認証ヘッダーを生成
const authHeader = 'Basic ' + btoa(`${username}:${password}`);
// POSTリクエストでOpenObserveにデータを送信
const response = await fetch(`https://${env.OPENOBSERVE_FQDN}/api/upload/logs/_json`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: authHeader,
},
body: fileContent,
});
// レスポンスのステータスコードを確認
if (!response.ok) {
console.error(`Failed to send logs to OpenObserve. Status: ${response.status}, Message: ${await response.text()}`);
throw new Error('Failed to send logs to OpenObserve');
}
console.log(`Successfully sent logs to OpenObserve: ${fileName}`);
},
} satisfies ExportedHandler<Env>;
`wrangler.json`を設定します。
※openobserveのFQDNは指定してください
また、queuesのmax_batch_size、max_batch_timeoutは環境に合わせて設定を行ってください。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "test-workers-r2-notifications-202501",
"main": "src/index.ts",
"compatibility_date": "2025-01-24",
"observability": {
"enabled": true
},
"queues": {
"consumers": [
{
"queue": "test-queue-r2-notifications-202501",
"max_batch_size": 3,
"max_batch_timeout": 10
}
]
},
"vars": {
"OPENOBSERVE_FQDN": "openobserveのFQDN"
}
}
cloudflareにdeployします。
$ npx wrangler deploy

Cloudflare secret作成
openobserveにデータを送信するために、Cloudflareのsecretにユーザ名とパスワードをセットしておきます。
$ npx wrangler secret put OPENOBSERVE_USERNAME
$ npx wrangler secret put OPENOBSERVE_PASSWORD

動作検証
上記で下準備が完了しました。ファイルをアップロードして動作を確認してみましょう。

openobserveにログインして、Organizationをuploadに変更します。
ログ項目からデータが送られていることを確認できます。

うまくデータが転送されてない場合は、ログをtailしてエラーが出ていないか確認してみてください。
下記は問題なく送信できた際のログになります。
$ npx wrangler tail
Successfully created tail, expires at 2025-01-28T03:24:41Z
Connected to test-workers-r2-notifications-202501, waiting for logs...
Queue test-queue-r2-notifications-202501 (3 messages) - Ok @ 2025/1/28 6:42:34
(log) Successfully sent logs to OpenObserve: upload-logs-2025-01-27T21-42-34-727Z.json
Queue test-queue-r2-notifications-202501 (2 messages) - Ok @ 2025/1/28 6:42:43
(log) Successfully sent logs to OpenObserve: upload-logs-2025-01-27T21-42-43-018Z.json
R2バケットにどれくらいファイルがアップロードされたか可視化することができました。
ファイルがどれくらい削除されたかの可視化を行いたい場合は、event notificationsにてファイルが削除された場合というルールが設定できますのでそちらを利用して可視化することができます。
2:ファイルがアップロードされたらSlack通知
例えばworkersでエラーが出た場合にエラー内容をR2バケットに保存している場合や、queuesが失敗してdead letter queueがバケットに格納された場合などに、すぐ気付けるようにslack通知できます。
構成図

secret削除
「1:アップロードしたファイル情報を分析サーバに転送(デリート時にも適用可能)」で登録したCloudflare secretはもう必要ないので削除しておきます
workersを編集
先ほどのworkersの`/src/index.ts`を編集します。
※Slack Webhook URLはご用意ください
export default {
async queue(batch, env): Promise<void> {
const slackWebhookUrl = 'https://hooks.slack.com/services/xxxxxxxxxxxxxxx';
// Slackに送信するメッセージ
const message = {
text: 'File uploaded to test-input-bucket-r2-notifications-202501 bucket. Please confirm.',
};
try {
// Slack WebhookにPOSTリクエストを送信
const slackResponse = await fetch(slackWebhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(message),
});
if (slackResponse.ok) {
console.log('Message sent to Slack successfully!');
} else {
const errorText = await slackResponse.text();
console.error(`Failed to send message to Slack: ${errorText}`);
}
} catch (error) {
console.error(`Error sending message to Slack: ${(error as Error).message}`);
}
},
} satisfies ExportedHandler<Env>;
`wrangler.json`を編集します。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "test-workers-r2-notifications-202501",
"main": "src/index.ts",
"compatibility_date": "2025-01-24",
"observability": {
"enabled": true
},
"queues": {
"consumers": [
{
"queue": "test-queue-r2-notifications-202501",
"max_batch_size": 3,
"max_batch_timeout": 10
}
]
}
}
cloudflareにdeployします。
$ npx wrangler deploy
動作検証
上記で下準備が完了しました。
テストなので適当なファイルをアップロードして動作を確認してみましょう。

少し待つとslackに通知が来ることを確認できました。

3:リサイズした画像をR2バケットに保存
R2にアップロードした画像ファイルをCloudflare imagesを使用してリサイズを行い、リサイズした画像を別のR2バケットに保存できます。
※Cloudflare imagesを契約している必要があります。
構成図

R2を公開バケットにする
動作確認用に画像を「test-input-bucket-r2-notifications-202501」バケットにアップロードしておきます。
※コードの関係上「.jpg」形式のものにしてください。

「test-input-bucket-r2-notifications-202501」バケットの設定からパブリックアクセスを有効にします。



Cloudflare DNSを確認するとレコードが作成されています。

CNAMEセットアップの環境の方は、権威DNSにてCloudflareにCNAMEを向けてください。

登録したFQDNにブラウザからアクセスして画像が見れることを確認します。

OUTPUT用のR2バケット作成
リサイズした画像を保存するR2バケットを作成します。
$ npx wrangler r2 bucket create test-output-bucket-r2-notifications-202501
R2バケットが作成されていることを確認します。

workersを編集
先ほどのworkersの`/src/index.ts`を編集します。
jpg画像だけをリサイズ対象としている内容になります。
Cloudflare imagesのURLを使用して画像サイズを変換する機能を使用しています。
詳細については下記URLをご確認ください。
Cloudflare doc:
https://developers.cloudflare.com/images/transform-images/transform-via-url/
export interface Env {
INPUT_BUCKET: R2Bucket;
OUTPUT_BUCKET: R2Bucket;
R2_DOMAIN: string;
}
export default {
async queue(batch, env: Env): Promise<void> {
for (const message of batch.messages) {
try {
console.log('Message body:', message.body);
const eventData = message.body as {
account: string;
bucket: string;
eventTime: string;
action: string;
object: {
key: string;
size: number;
eTag: string;
};
};
const objectKey = eventData.object.key;
console.log(`Processing image: ${objectKey}`);
// ファイル拡張子が jpg であることを確認
if (!objectKey.toLowerCase().endsWith('.jpg') && !objectKey.toLowerCase().endsWith('.jpeg')) {
console.log(`File ${objectKey} is not a JPG. Skipping processing.`);
continue; // JPG以外の場合はスキップ
}
const imageUrl = `https://${env.R2_DOMAIN}/cdn-cgi/image/width=200,quality=40/https://${env.R2_DOMAIN}/${objectKey}`;
console.log(`imageUrl: ${imageUrl}`);
const resizedImageResponse = await fetch(imageUrl);
if (!resizedImageResponse.ok) {
throw new Error(`Failed to resize image: ${resizedImageResponse.statusText}`);
}
const resizedImage = await resizedImageResponse.arrayBuffer();
// OUTPUT_BUCKET にリサイズ画像を保存
const resizedKey = `resized/${objectKey}`;
await env.OUTPUT_BUCKET.put(resizedKey, resizedImage, {
httpMetadata: {
contentType: 'image/jpeg',
},
});
console.log(`Image resized and saved: ${resizedKey}`);
} catch (error) {
console.error('Error processing message:', error);
}
}
},
} satisfies ExportedHandler<Env>;
`wrangler.json`を編集します。
公開R2バケットのFQDNは環境に合わせて設定を行って下さい。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "test-workers-r2-notifications-202501",
"main": "src/index.ts",
"compatibility_date": "2025-01-24",
"observability": {
"enabled": true
},
"queues": {
"consumers": [
{
"queue": "test-queue-r2-notifications-202501",
"max_batch_size": 3,
"max_batch_timeout": 10
}
]
},
"r2_buckets": [
{
"binding": "INPUT_BUCKET",
"bucket_name": "test-input-bucket-r2-notifications-202501"
},
{
"binding": "OUTPUT_BUCKET",
"bucket_name": "test-output-bucket-r2-notifications-202501"
}
],
"vars": {
"R2_DOMAIN": "公開R2バケットのFQDN"
}
}
cloudflareにdeployします。
$ npx wrangler deploy
動作検証
上記で下準備が完了しました。
表示テストした画像ファイルを名前変更して「test-input-bucket-r2-notifications-202501」バケットにアップロードをしてみます。
※画像ファイル名を002.jpgにしています。

少し待つと、「test-output-bucket-r2-notifications-202501」バケットにリサイズされたファイルが作成されます。

アップロードしたファイルサイズと比べてみて、サイズが小さくなっています。
ファイルをダウンロードして確認してみると、サイズが変更されていることが確認できます。

最後に
R2でイベントドリブンな処理が実行できるようになり、より便利になりました。
簡単に思いつく内容を検証しましたが、Workersで処理することになりますので色々なことができそうです。
弊社でCloudflareをご契約のお客様でしたら、こういう処理はできないのか検証してほしいなどのご相談もお受けできますのでお気軽に聞いていただけると幸いです。
そしてCloudflareなら上記以外でも、
・パフォーマンスの向上
・サイトの信頼性の向上
・セキュリティの向上
が、一つのサービスで実現できますので非常におすすめです。
Cloudflareに、アクセリアの運用サポートをプラスしたCDNサービスを提供しています
移行支援によるスムーズな導入とともに、お客様の運用負担を最小限にとどめながら、WEBサイトのパフォーマンスとセキュリティを最大限に高めます。運用サポートはフルアウトソーシングからミニマムサポートまで、ご要望に合わせてご提供します。
Cloudflare(クラウドフレア)の導入や運用について、またそれ以外のことでもなにか気になることがございましたらお気軽にご相談下さい。
サービスにご興味をお持ちの方は
お気軽にお問い合わせください。
Webからお問い合わせ
お問い合わせお電話からお問い合わせ
平日09:30 〜 18:00
Free Service