INTEGRITY Dokumentace

Použití oznámení o události k shrnutí souborů PDF při nahrání

V tomto tutoriálu se naučíte používat oznámení o událostech ke zpracování souboru PDF při jeho nahrání do R2 bucketu. Použijete Workers AI k vytvoření souhrnu PDF a jeho uložení jako textového souboru ve stejném bucketu.

Předpoklady

Pro pokračování budete potřebovat:

Správce verzí Node.js

Použijte správce verzí Node, jako je Volta nebo nvm abyste se vyhnuli problémům s oprávněními a měnili verze Node.js. Wrangler, o kterém se dále hovoří v tomto průvodci, vyžaduje verzi Node 16.17.0 nebo novější.

1. Vytvořte nový projekt

Vytvoříte nový projekt Worker, který bude používat Static Assets k obsluze frontendu vaší aplikace. Uživatel může pomocí tohoto frontendu nahrát soubor PDF, který následně zpracuje váš Worker.

Vytvořte nový projekt Worker spuštěním následujících příkazů:

npm create cloudflare@latest -- pdf-summarizer

Při nastavení vyberte následující možnosti:

Přejděte na pdf-summarizer adresář:

cd pdf-summarizer

2. Vytvořte front-end

Pomocí Static Assets můžete servírovat frontend své aplikace přímo z Workeru. Chcete-li Static Assets použít, musíte do souboru Wrangler přidat požadované bindings.

{
	"assets": {
		"directory": "public"
	}
}
[assets]
directory = "public"

Dále vytvořte public adresáře a přidejte index.html soubor. index.html soubor by měl obsahovat následující kód HTML:

Vyberte pro zobrazení kódu 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>

Chcete-li zobrazit frontend aplikace, spusťte následující příkaz a přejděte na URL adresu zobrazenou v terminálu:

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              │
╰───────────────────────────╯

Když otevřete tuto URL adresu v prohlížeči, uvidíte formulář pro nahrání souboru. Pokud zkusíte soubor nahrát, zjistíte, že se na server nenahraje. Je to proto, že frontend zatím není propojen s backendem. V dalším kroku upravíte svůj Worker tak, aby nahrávání souboru zpracovával.

3. Zpracujte nahrávání souborů

Chcete-li zpracovat nahrávání souborů, musíte nejprve přidat R2 binding. Do souboru Wrangler přidejte následující kód:

{
	"r2_buckets": [
		{
			"binding": "MY_BUCKET",
			"bucket_name": "<R2_BUCKET_NAME>"
		}
	]
}
[[r2_buckets]]
binding = "MY_BUCKET"
bucket_name = "<R2_BUCKET_NAME>"

Nahraďte <R2_BUCKET_NAME> s názvem vašeho bucketu R2.

Dále aktualizujte src/index.ts soubor. src/index.ts soubor by měl obsahovat následující kód:

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>;

Výše uvedený kód provádí následující:

Vzhledem k tomu, že kód Workeru je napsán v TypeScriptu, spusťte následující příkaz pro přidání potřebných definic typů. Není to sice povinné, ale pomůže vám to předejít chybám.

npm run cf-typegen

Vývojový server můžete restartovat, abyste změny otestovali:

npm run dev

4. Vytvořte queue

Oznámení o událostech zaznamenávají změny dat ve vašem bucketu R2. Budete muset vytvořit novou frontu pdf-summarize pro příjem oznámení:

npx wrangler queues create pdf-summarizer

Přidejte vazbu do souboru Wrangler:

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

5. Zpracujte event notifications

Nyní, když máte frontu pro příjem oznámení o událostech, je potřeba upravit Worker tak, aby tato oznámení zpracovával. Budete muset přidat Queue handler, který z PDF extrahuje textový obsah, pomocí Workers AI jej shrne a poté uloží do bucketu R2.

Aktualizujte src/index.ts soubor, do kterého přidáte handler 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>;

Výše uvedený kód provádí následující:

Prozatím queue handler zatím nic nedělá. V dalších krocích upravíte queue handler tak, aby extrahoval textový obsah z PDF, shrnul ho pomocí Workers AI a poté ho přidal do bucketu.

6. Extrahujte textový obsah z PDF

Chcete-li extrahovat textový obsah z PDF, Worker použije unpdf knihovna. unpdf knihovna poskytuje nástroje pro práci se soubory PDF.

Nainstalujte unpdf knihovnu nainstalujte pomocí následujícího příkazu:

npm i unpdf

Aktualizujte src/index.ts soubor, do kterého importujete potřebné moduly z unpdf knihovna:

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

Dále aktualizujte queue handler pro extrakci textového obsahu z PDF:

src/index.ts
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)}...`);
    }
}

Výše uvedený kód provádí následující:

7. Použijte Workers AI k shrnutí obsahu

Chcete-li používat Workers AI, musíte do souboru Wrangler přidat binding Workers AI. Soubor Wrangler by měl obsahovat následující kód:

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

Definici typu AI přidáte spuštěním následujícího příkazu:

npm run cf-typegen

Aktualizujte src/index.ts soubor tak, aby pro shrnutí obsahu používal Workers AI:

src/index.ts
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 handler nyní používá Workers AI k shrnutí obsahu.

8. Přidejte shrnutí do R2 bucketu

Nyní, když máte shrnutí, je potřeba jej přidat do bucketu R2. Upravte src/index.ts soubor, do kterého přidáte shrnutí do R2 bucketu:

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}`);
  }
}

Handler fronty nyní přidá souhrn do bucketu R2 jako textový soubor.

9. Povolte event notifications

Váš queue handler je připraven zpracovávat příchozí zprávy oznámení o událostech. Nyní musíte povolit oznámení o událostech pomocí wrangler r2 bucket notification create příkaz pro váš bucket. Následující příkaz vytvoří oznámení o události pro object-create typ události pro pdf přípona:

npx wrangler r2 bucket notification create <R2_BUCKET_NAME> --event-type object-create --queue pdf-summarizer --suffix "pdf"

Nahraďte <R2_BUCKET_NAME> s názvem vašeho bucketu R2.

Oznámení o události se vytvoří pro pdf přípona. Když je nahrán nový soubor s pdf přípona je nahrána do R2 bucketu, pdf-summarizer fronta je aktivována.

10. Nasaďte svůj Worker

Chcete-li nasadit svého Workera, spusťte wrangler deploy příkaz:

npx wrangler deploy

Ve výstupu wrangler deploy zkopírujte URL. Toto je URL vaší nasazené aplikace.

11. Otestujte

Chcete-li aplikaci otestovat, přejděte na URL adresu nasazené aplikace a nahrajte soubor PDF. Případně můžete použít Cloudflare dashboard k nahrání souboru PDF.

Chcete-li zobrazit protokoly, můžete použít wrangler tail příkazu.

npx wrangler tail

Logy uvidíte ve svém terminálu. Můžete také přejít do Cloudflare dashboardu a zobrazit logy v sekci Workers Logs.

Pokud zkontrolujete svůj bucket R2, uvidíte souhrnný soubor.

Závěr

V tomto tutoriálu jste se naučili používat R2 event notifications ke zpracování objektu při nahrání. Vytvořili jste aplikaci pro nahrávání PDF souboru a konzumentského Workera, který vytváří shrnutí PDF souboru. Naučili jste se také použít Workers AI ke shrnutí obsahu PDF souboru a nahrání tohoto shrnutí do bucketu R2.

Stejný postup můžete použít i pro zpracování dalších typů souborů, jako jsou obrázky, videa a zvukové soubory. Stejně tak jej můžete použít pro zpracování dalších typů událostí, jako je smazání nebo aktualizace objektu.

Pokud si chcete prohlédnout kód k tomuto tutoriálu, najdete jej na GitHub.