תיעוד 259
שימוש בהתראת אירוע כדי לסכם קובצי PDF בעת העלאה
במדריך זה תלמדו כיצד להשתמש בהתראות אירוע כדי לעבד קובץ PDF כאשר הוא מועלה ל-bucket של R2. תשתמשו בWorkers AI כדי לסכם את ה-PDF ולשמור את הסיכום כקובץ טקסט באותו bucket.
#דרישות מוקדמות
כדי להמשיך, תצטרכו:
- חשבון Cloudflare ↗︎ עם גישה ל-R2.
- bucket קיים של R2. עיינו במדריך תחילת העבודה של R2.
- התקנת
Node.js↗︎.
#מנהל גרסאות של Node.js
השתמשו ב-Vite+ (vp env) ↗︎ כדי להתקין ולנהל גרסאות של Node.js.
לגרסאות נתמכות של Node.js, עיינו בדרישות המערכת של Wrangler.
#1. יצירת פרויקט חדש
תיצרו פרויקט Worker חדש שישתמש בStatic Assets כדי להגיש את ה-front-end של היישום. משתמש יכול להעלות קובץ PDF באמצעות ה-front-end הזה, ואז ה-Worker יעבד אותו.
צרו פרויקט Worker חדש על ידי הרצת הפקודות הבאות:
npm:
npm create cloudflare@latest -- pdf-summarizeryarn:
yarn create cloudflare pdf-summarizerpnpm:
pnpm 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 add an AGENTS.md file to help AI coding tools understand Cloudflare APIs?, בחרו
Yes. - עבור Do you want to deploy your application?, בחרו
No(נבצע כמה שינויים לפני הפריסה).
עברו לספרייה pdf-summarizer:
cd pdf-summarizer#2. יצירת ה-front-end
באמצעות Static Assets אפשר להגיש את ה-front-end של היישום מה-Worker. כדי להשתמש ב-Static Assets, צריך להוסיף את ה-bindings הנדרשים לקובץ 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>כדי להציג את ה-front-end של היישום, הריצו את הפקודה הבאה ועברו לכתובת שמוצגת בטרמינל:
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 │
╰───────────────────────────╯כאשר תפתחו את הכתובת בדפדפן, תראו שיש טופס להעלאת קובץ. אם תנסו להעלות קובץ, תשימו לב שהקובץ לא מועלה לשרת. הסיבה היא שה-front-end לא מחובר ל-back-end. בשלב הבא תעדכנו את ה-Worker שיטפל בהעלאת הקובץ.
#3. טיפול בהעלאת קובץ
כדי לטפל בהעלאת הקובץ, תחילה צריך להוסיף את ה-binding של 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> בשם של ה-bucket של R2.
לאחר מכן, עדכנו את הקובץ src/index.ts. הקובץ src/index.ts צריך להכיל את הקוד הבא:
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>;הקוד שלמעלה עושה את הפעולות הבאות:
- בודק אם הבקשה היא בקשת POST לנקודת הקצה
/api/upload. אם כן, הוא מקבל את הקובץ מהבקשה ומעלה אותו ל-Cloudflare R2 באמצעות Workers API. - אם הבקשה אינה בקשת POST לנקודת הקצה
/api/upload, הוא מחזיר תגובת 404.
מכיוון שקוד ה-Worker כתוב ב-TypeScript, כדאי להריץ את הפקודה הבאה כדי להוסיף את הגדרות הטיפוס הנדרשות. זה לא חובה, אבל זה יעזור להימנע משגיאות.
#מניעת שגיאות אפשריות בגישה ל-request.body
אפשר לגשת לגוף של Request ↗︎ רק פעם אחת. אם השתמשתם קודם ב-request.formData() באותה בקשה, ייתכן שתיתקלו ב-TypeError כאשר תנסו לגשת ל-request.body.
כדי להימנע משגיאות, צרו עותק של אובייקט ה-Request באמצעות request.clone() לכל ניסיון נוסף לגשת לגוף של Request. קחו בחשבון של-Workers יש מגבלת זיכרון של 128 MB לכל Worker, וטעינה של קבצים גדולים במיוחד לזיכרון של Worker כמה פעמים עלולה להגיע למגבלה הזו. כדי לוודא ששימוש הזיכרון לא מגיע למגבלה הזו, שקלו להשתמש ב-Streams.
npm run cf-typegenאפשר להפעיל מחדש את שרת הפיתוח כדי לבדוק את השינויים:
npm run dev#4. יצירת תור
התראות אירוע לוכדות שינויים בנתונים ב-bucket של R2. תצטרכו ליצור תור חדש pdf-summarize כדי לקבל התראות:
npx wrangler queues create pdf-summarizerהוסיפו את ה-binding לקובץ Wrangler:
{
"queues": {
"consumers": [
{
"queue": "pdf-summarizer"
}
]
}
}[[queues.consumers]]
queue = "pdf-summarizer"#5. טיפול בהתראות אירוע
עכשיו, כשיש תור לקבלת התראות אירוע, צריך לעדכן את ה-Worker כדי לטפל בהתראות האירוע. תצטרכו להוסיף Queue handler שיחלץ את התוכן הטקסטואלי מה-PDF, ישתמש ב-Workers AI כדי לסכם את התוכן, ואז ישמור אותו ב-bucket של R2.
עדכנו את הקובץ src/index.ts כדי להוסיף את ה-Queue handler:
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>;הקוד שלמעלה עושה את הפעולות הבאות:
- ה-handler של
queueנקרא כאשר הודעה חדשה מתווספת לתור. הוא עובר על ההודעות ב-batch ורושם ביומן את שם הקובץ.
לעת עתה ה-handler של queue לא עושה דבר. בשלבים הבאים תעדכנו את ה-handler של queue כדי לחלץ את התוכן הטקסטואלי מה-PDF, להשתמש ב-Workers AI כדי לסכם את התוכן, ואז להוסיף אותו ל-bucket.
#6. חילוץ התוכן הטקסטואלי מה-PDF
כדי לחלץ את התוכן הטקסטואלי מה-PDF, ה-Worker ישתמש בספרייה unpdf ↗︎. הספרייה unpdf מספקת כלי עזר לעבודה עם קובצי PDF.
התקינו את הספרייה unpdf על ידי הרצת הפקודה הבאה:
npm:
npm i unpdfyarn:
yarn add unpdfpnpm:
pnpm add unpdfbun:
bun add unpdfעדכנו את הקובץ src/index.ts כדי לייבא את המודולים הנדרשים מספריית unpdf:
src/index.ts:
import { extractText, getDocumentProxy } from "unpdf";לאחר מכן, עדכנו את ה-handler של queue כדי לחלץ את התוכן הטקסטואלי מה-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)}...`);
}
}הקוד שלמעלה עושה את הפעולות הבאות:
- ה-handler של
queueמקבל את הקובץ מה-bucket של R2. - ה-handler של
queueמחלץ את התוכן הטקסטואלי מה-PDF באמצעות הספרייהunpdf. - ה-handler של
queueרושם ביומן את התוכן הטקסטואלי.
#7. שימוש ב-Workers AI כדי לסכם את התוכן
כדי להשתמש ב-Workers AI, תצטרכו להוסיף את ה-binding של Workers AI לקובץ Wrangler. קובץ Wrangler צריך להכיל את הקוד הבא:
{
"ai": {
"binding": "AI"
}
}[ai]
binding = "AI"הריצו את הפקודה הבאה כדי להוסיף את הגדרת הטיפוס של AI:
npm run cf-typegenעדכנו את הקובץ src/index.ts כדי להשתמש ב-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)}...`);
}
}ה-handler של queue משתמש עכשיו ב-Workers AI כדי לסכם את התוכן.
#8. הוספת הסיכום ל-bucket של R2
עכשיו, כשיש את הסיכום, צריך להוסיף אותו ל-bucket של R2. עדכנו את הקובץ src/index.ts כדי להוסיף את הסיכום ל-bucket של 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}`);
}
}ה-handler של התור מוסיף עכשיו את הסיכום ל-bucket של R2 כקובץ טקסט.
#9. הפעלת התראות אירוע
ה-handler של queue מוכן לטפל בהודעות התראת אירוע נכנסות. צריך להפעיל התראות אירוע באמצעות הפקודה wrangler r2 bucket notification create עבור ה-bucket. הפקודה הבאה יוצרת התראת אירוע עבור סוג האירוע object-create עבור הסיומת pdf:
npx wrangler r2 bucket notification create <R2_BUCKET_NAME> --event-type object-create --queue pdf-summarizer --suffix "pdf"החליפו את <R2_BUCKET_NAME> בשם של ה-bucket של R2.
נוצרת התראת אירוע עבור הסיומת pdf. כאשר קובץ חדש עם הסיומת pdf מועלה ל-bucket של R2, התור pdf-summarizer מופעל.
#10. פריסת ה-Worker
כדי לפרוס את ה-Worker, הריצו את הפקודה wrangler deploy:
npx wrangler deployבפלט של הפקודה wrangler deploy, העתיקו את הכתובת. זו הכתובת של היישום הפרוס.
#11. בדיקה
כדי לבדוק את היישום, עברו לכתובת של היישום הפרוס והעלו קובץ PDF. לחלופין, אפשר להשתמש בלוח הבקרה של Cloudflare ↗︎ כדי להעלות קובץ PDF.
כדי להציג את היומנים, אפשר להשתמש בפקודה wrangler tail.
npx wrangler tailתראו את היומנים בטרמינל. אפשר גם לעבור ללוח הבקרה של Cloudflare ולהציג את היומנים במקטע Workers Logs.
אם תבדקו את ה-bucket של R2, תראו את קובץ הסיכום.
#סיכום
במדריך זה למדתם כיצד להשתמש בהתראות אירוע של R2 כדי לעבד אובייקט בעת העלאה. יצרתם יישום להעלאת קובץ PDF, ויצרתם Worker צרכן שיוצר סיכום של קובץ ה-PDF. למדתם גם כיצד להשתמש ב-Workers AI כדי לסכם את התוכן של קובץ ה-PDF, ולהעלות את הסיכום ל-bucket של R2.
אפשר להשתמש באותה גישה כדי לעבד סוגים אחרים של קבצים, כמו תמונות, סרטונים וקובצי שמע. אפשר גם להשתמש באותה גישה כדי לעבד סוגים אחרים של אירועים, כמו מחיקת אובייקט ועדכון אובייקט.
אם תרצו להציג את הקוד של המדריך הזה, אפשר למצוא אותו ב-GitHub ↗︎.