Skip to content

非公式本サイトは非公式の日本語ドキュメントであり、Cloudflare 公式サイトではありません。最新情報はdevelopers.cloudflare.comをご確認ください。

イベント通知でアップロード時に PDF を要約する

最終更新 Markdown で表示Agent セットアップ

このチュートリアルでは、PDF が R2 バケットへアップロードされたときに イベント通知 で処理する方法を学びます。Workers AI で PDF を要約し、同じバケットにテキストファイルとして保存します。

前提条件

次のものが必要です。

Node.js のバージョン管理

権限の問題を避け、Node.js のバージョンを切り替えるには、Voltanvm などの Node バージョンマネージャーを使います。このガイドの後半で扱う Wrangler には、Node バージョン 16.17.0 以降が必要です。

1. 新しいプロジェクトを作成する

Static Assets でアプリケーションのフロントエンドを配信する、新しい Worker プロジェクトを作成します。ユーザーはこのフロントエンドから PDF をアップロードし、Worker が処理します。

次のコマンドで、新しい Worker プロジェクトを作成します。

npm 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-summarizer

2. フロントエンドを作成する

Static 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 を更新します。

3. ファイルアップロードを処理する

ファイルアップロードを処理するには、まず 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 ファイルを更新します。内容は次のとおりです。

src/index.tsts
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

4. キューを作成する

イベント通知は、R2 バケット内のデータの変更を捕捉します。通知を受け取る新しいキュー pdf-summarize を作成します。

npx wrangler queues create pdf-summarizer

Wrangler ファイルにバインディングを追加します。

{
	"queues": {
		"consumers": [
			{
				"queue": "pdf-summarizer"
			}
		]
	}
}
[[queues.consumers]]
queue = "pdf-summarizer"

5. イベント通知を処理する

イベント通知を受け取るキューができたら、Worker を更新してイベント通知を処理します。PDF からテキストを抽出し、Workers AI で要約し、R2 バケットへ保存する Queue ハンドラーを追加します。

Queue ハンドラーを追加するため、src/index.ts を更新します。

src/index.tsts
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 ハンドラーを更新します。

6. PDF からテキストを抽出する

PDF からテキストを抽出するため、Worker は unpdf ライブラリを使います。unpdf は PDF ファイル向けのユーティリティを提供します。

次のコマンドで unpdf ライブラリをインストールします。

npm i unpdf

unpdf ライブラリから必要なモジュールをインポートするため、src/index.ts を更新します。

src/index.tsts
import { extractText, getDocumentProxy } from "unpdf";

次に、PDF からテキストを抽出するように queue ハンドラーを更新します。

src/index.tsts
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 ハンドラーはテキストをログに出力します。

7. Workers AI で内容を要約する

Workers AI を使うには、Wrangler ファイルに Workers AI バインディングを追加します。内容は次のとおりです。

{
	"ai": {
		"binding": "AI"
	}
}
[ai]
binding = "AI"

AI の型定義を追加するには、次のコマンドを実行します。

npm run cf-typegen

内容を要約するため、src/index.ts を更新して Workers AI を使います。

src/index.tsts
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 で内容を要約するようになりました。

8. 要約を R2 バケットへ追加する

要約ができたら、R2 バケットへ追加します。要約を追加するため、src/index.ts を更新します。

src/index.tsts
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 バケットへ追加するようになりました。

9. イベント通知を有効にする

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 キューがトリガーされます。

10. Worker をデプロイする

Worker をデプロイするには、wrangler deploy コマンドを実行します。

npx wrangler deploy

wrangler deploy コマンドの出力から URL をコピーします。これがデプロイしたアプリケーションの URL です。

11. テストする

アプリケーションをテストするには、デプロイしたアプリケーションの URL を開き、PDF ファイルをアップロードします。または、Cloudflare ダッシュボード から PDF をアップロードすることもできます。

ログを確認するには、wrangler tail コマンドを使います。

npx wrangler tail

ターミナルにログが表示されます。Cloudflare ダッシュボードの Workers Logs セクションでもログを確認できます。

R2 バケットを確認すると、要約ファイルがあります。

まとめ

このチュートリアルでは、アップロード時にオブジェクトを処理するため、R2 のイベント通知を使う方法を学びました。PDF をアップロードするアプリケーションと、PDF の要約を作成するコンシューマー Worker を作成しました。Workers AI で PDF の内容を要約し、要約を R2 バケットへアップロードする方法も学びました。

同じ方法で、画像、動画、音声など、他の種類のファイルも処理できます。オブジェクトの削除や更新など、他の種類のイベントにも同じ方法を使えます。

このチュートリアルのコードは GitHub で確認できます。

役に立ちましたか?