このチュートリアルでは、PDF が R2 バケットへアップロードされたときに イベント通知 で処理する方法を学びます。Workers AI で PDF を要約し、同じバケットにテキストファイルとして保存します。
次のものが必要です。
- R2 を利用できる Cloudflare アカウント ↗
- 既存の R2 バケット。作成手順は R2 の始め方 を参照してください。
Node.js↗ をインストールします。
Node.js のバージョン管理
権限の問題を避け、Node.js のバージョンを切り替えるには、Volta ↗ や
nvm ↗ などの Node バージョンマネージャーを使います。このガイドの後半で扱う
Wrangler には、Node バージョン 16.17.0 以降が必要です。
Static Assets でアプリケーションのフロントエンドを配信する、新しい Worker プロジェクトを作成します。ユーザーはこのフロントエンドから PDF をアップロードし、Worker が処理します。
次のコマンドで、新しい Worker プロジェクトを作成します。
npm create cloudflare@latest -- pdf-summarizeryarn create cloudflare pdf-summarizerpnpm create cloudflare@latest pdf-summarizerセットアップでは、次のオプションを選びます。
- What would you like to start with? では、
Hello World exampleを選びます。 - Which template would you like to use? では、
Worker onlyを選びます。 - Which language do you want to use? では、
TypeScriptを選びます。 - Do you want to use git for version control? では、
Yesを選びます。 - Do you want to deploy your application? では、
Noを選びます(デプロイ前にいくつか変更します)。
pdf-summarizer ディレクトリへ移動します。
cd pdf-summarizerStatic Assets を使うと、Worker からアプリケーションのフロントエンドを配信できます。Static Assets を使うには、Wrangler ファイルに必要なバインディングを追加します。
{
"assets": {
"directory": "public"
}
}[assets]
directory = "public"次に、public ディレクトリを作成し、index.html ファイルを追加します。index.html には次の HTML を入れます。
HTML コードを表示する
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PDF Summarizer</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
min-height: 100vh;
margin: 0;
background-color: #fefefe;
}
.content {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.upload-container {
background-color: #f0f0f0;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.upload-button {
background-color: #4caf50;
color: white;
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.upload-button:hover {
background-color: #45a049;
}
footer {
background-color: #f0f0f0;
color: white;
text-align: center;
padding: 10px;
width: 100%;
}
footer a {
color: #333;
text-decoration: none;
margin: 0 10px;
}
footer a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="content">
<div class="upload-container">
<h2>Upload PDF File</h2>
<form id="uploadForm" onsubmit="return handleSubmit(event)">
<input
type="file"
id="pdfFile"
name="pdfFile"
accept=".pdf"
required
/>
<button type="submit" id="uploadButton" class="upload-button">
Upload
</button>
</form>
</div>
</div>
<footer>
<a
href="https://developers.cloudflare.com/r2/buckets/event-notifications/"
target="_blank"
>R2 Event Notification</a
>
<a
href="https://developers.cloudflare.com/queues/get-started/#3-create-a-queue"
target="_blank"
>Cloudflare Queues</a
>
<a href="https://developers.cloudflare.com/workers-ai/" target="_blank"
>Workers AI</a
>
<a
href="https://github.com/harshil1712/pdf-summarizer-r2-event-notification"
target="_blank"
>GitHub Repo</a
>
</footer>
<script>
handleSubmit = async (event) => {
event.preventDefault();
// Disable the upload button and show a loading message
const uploadButton = document.getElementById("uploadButton");
uploadButton.disabled = true;
uploadButton.textContent = "Uploading...";
// get form data
const formData = new FormData(event.target);
const file = formData.get("pdfFile");
if (file) {
// call /api/upload endpoint and send the file
await fetch("/api/upload", {
method: "POST",
body: formData,
});
event.target.reset();
} else {
console.log("No file selected");
}
uploadButton.disabled = false;
uploadButton.textContent = "Upload";
};
</script>
</body>
</html>アプリケーションのフロントエンドを確認するには、次のコマンドを実行し、ターミナルに表示された URL を開きます。
npm run dev ⛅️ wrangler 3.80.2
-------------------
⎔ Starting local server...
[wrangler:inf] Ready on http://localhost:8787
╭───────────────────────────╮
│ [b] open a browser │
│ [d] open devtools │
│ [l] turn off local mode │
│ [c] clear console │
│ [x] to exit │
╰───────────────────────────╯ブラウザーで URL を開くと、ファイルアップロードフォームが表示されます。ファイルをアップロードしても、サーバーには送られません。フロントエンドがバックエンドに接続されていないためです。次のステップで、ファイルアップロードを処理する Worker を更新します。
ファイルアップロードを処理するには、まず R2 バインディングを追加します。Wrangler ファイルに次のコードを追加します。
{
"r2_buckets": [
{
"binding": "MY_BUCKET",
"bucket_name": "<R2_BUCKET_NAME>"
}
]
}[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "<R2_BUCKET_NAME>"<R2_BUCKET_NAME> を、自分の R2 バケット名に置き換えます。
次に、src/index.ts ファイルを更新します。内容は次のとおりです。
export default {
async fetch(request, env, ctx): Promise<Response> {
// Get the pathname from the request
const pathname = new URL(request.url).pathname;
if (pathname === "/api/upload" && request.method === "POST") {
// Get the file from the request
const formData = await request.formData();
const file = formData.get("pdfFile") as File;
// Upload the file to Cloudflare R2
const upload = await env.MY_BUCKET.put(file.name, file);
return new Response("File uploaded successfully", { status: 200 });
}
return new Response("incorrect route", { status: 404 });
},
} satisfies ExportedHandler<Env>;上記のコードは次のことを行います。
- リクエストが
/api/uploadエンドポイントへの POST かどうかを確認します。該当する場合、リクエストからファイルを取得し、Workers API で Cloudflare R2 へアップロードします。 - リクエストが
/api/uploadへの POST でない場合は、404 レスポンスを返します。
Worker のコードは TypeScript なので、必要な型定義を追加するには次のコマンドを実行します。必須ではありませんが、エラーを避けやすくなります。
npm run cf-typegen変更を確認するには、開発サーバーを再起動します。
npm run devイベント通知は、R2 バケット内のデータの変更を捕捉します。通知を受け取る新しいキュー pdf-summarize を作成します。
npx wrangler queues create pdf-summarizerWrangler ファイルにバインディングを追加します。
{
"queues": {
"consumers": [
{
"queue": "pdf-summarizer"
}
]
}
}[[queues.consumers]]
queue = "pdf-summarizer"イベント通知を受け取るキューができたら、Worker を更新してイベント通知を処理します。PDF からテキストを抽出し、Workers AI で要約し、R2 バケットへ保存する Queue ハンドラーを追加します。
Queue ハンドラーを追加するため、src/index.ts を更新します。
export default {
async fetch(request, env, ctx): Promise<Response> {
// No changes in the fetch handler
},
async queue(batch, env) {
for (let message of batch.messages) {
console.log(`Processing the file: ${message.body.object.key}`);
}
},
} satisfies ExportedHandler<Env>;上記のコードは次のことを行います。
- キューに新しいメッセージが追加されると、
queueハンドラーが呼ばれます。バッチ内のメッセージをループし、ファイル名をログに出力します。
いまの queue ハンドラーは、まだ何もしていません。次のステップで、PDF からテキストを抽出し、Workers AI で要約し、バケットへ追加するように queue ハンドラーを更新します。
PDF からテキストを抽出するため、Worker は unpdf ↗ ライブラリを使います。unpdf は PDF ファイル向けのユーティリティを提供します。
次のコマンドで unpdf ライブラリをインストールします。
npm i unpdfyarn add unpdfpnpm add unpdfbun add unpdfunpdf ライブラリから必要なモジュールをインポートするため、src/index.ts を更新します。
import { extractText, getDocumentProxy } from "unpdf";次に、PDF からテキストを抽出するように queue ハンドラーを更新します。
async queue(batch, env) {
for(let message of batch.messages) {
console.log(`Processing file: ${message.body.object.key}`);
// Get the file from the R2 bucket
const file = await env.MY_BUCKET.get(message.body.object.key);
if (!file) {
console.error(`File not found: ${message.body.object.key}`);
continue;
}
// Extract the textual content from the PDF
const buffer = await file.arrayBuffer();
const document = await getDocumentProxy(new Uint8Array(buffer));
const {text} = await extractText(document, {mergePages: true});
console.log(`Extracted text: ${text.substring(0, 100)}...`);
}
}上記のコードは次のことを行います。
queueハンドラーは R2 バケットからファイルを取得します。queueハンドラーはunpdfライブラリで PDF からテキストを抽出します。queueハンドラーはテキストをログに出力します。
Workers AI を使うには、Wrangler ファイルに Workers AI バインディングを追加します。内容は次のとおりです。
{
"ai": {
"binding": "AI"
}
}[ai]
binding = "AI"AI の型定義を追加するには、次のコマンドを実行します。
npm run cf-typegen内容を要約するため、src/index.ts を更新して Workers AI を使います。
async queue(batch, env) {
for(let message of batch.messages) {
// Extract the textual content from the PDF
const {text} = await extractText(document, {mergePages: true});
console.log(`Extracted text: ${text.substring(0, 100)}...`);
// Use Workers AI to summarize the content
const result: AiSummarizationOutput = await env.AI.run(
"@cf/facebook/bart-large-cnn",
{
input_text: text,
}
);
const summary = result.summary;
console.log(`Summary: ${summary.substring(0, 100)}...`);
}
}queue ハンドラーは、Workers AI で内容を要約するようになりました。
要約ができたら、R2 バケットへ追加します。要約を追加するため、src/index.ts を更新します。
async queue(batch, env) {
for(let message of batch.messages) {
// Extract the textual content from the PDF
// ...
// Use Workers AI to summarize the content
// ...
// Add the summary to the R2 bucket
const upload = await env.MY_BUCKET.put(`${message.body.object.key}-summary.txt`, summary, {
httpMetadata: {
contentType: 'text/plain',
},
});
console.log(`Summary added to the R2 bucket: ${upload.key}`);
}
}queue ハンドラーは、要約をテキストファイルとして R2 バケットへ追加するようになりました。
queue ハンドラーは、受信するイベント通知メッセージを処理できる状態です。バケットでイベント通知を有効にするには、wrangler r2 bucket notification create コマンド を使います。次のコマンドは、pdf サフィックスに対する object-create イベント種別のイベント通知を作成します。
npx wrangler r2 bucket notification create <R2_BUCKET_NAME> --event-type object-create --queue pdf-summarizer --suffix "pdf"<R2_BUCKET_NAME> を、自分の R2 バケット名に置き換えます。
pdf サフィックス向けのイベント通知が作成されます。pdf サフィックスの新しいファイルが R2 バケットへアップロードされると、pdf-summarizer キューがトリガーされます。
Worker をデプロイするには、wrangler deploy コマンドを実行します。
npx wrangler deploywrangler deploy コマンドの出力から URL をコピーします。これがデプロイしたアプリケーションの URL です。
アプリケーションをテストするには、デプロイしたアプリケーションの URL を開き、PDF ファイルをアップロードします。または、Cloudflare ダッシュボード ↗ から PDF をアップロードすることもできます。
ログを確認するには、wrangler tail コマンドを使います。
npx wrangler tailターミナルにログが表示されます。Cloudflare ダッシュボードの Workers Logs セクションでもログを確認できます。
R2 バケットを確認すると、要約ファイルがあります。
このチュートリアルでは、アップロード時にオブジェクトを処理するため、R2 のイベント通知を使う方法を学びました。PDF をアップロードするアプリケーションと、PDF の要約を作成するコンシューマー Worker を作成しました。Workers AI で PDF の内容を要約し、要約を R2 バケットへアップロードする方法も学びました。
同じ方法で、画像、動画、音声など、他の種類のファイルも処理できます。オブジェクトの削除や更新など、他の種類のイベントにも同じ方法を使えます。
このチュートリアルのコードは GitHub ↗ で確認できます。