はじめに
文書添削AIツールを開発する中で、PDFファイルのアップロードと解析機能を実装することになりました。この記事では、特にFirebase Functions側の実装に焦点を当てて、開発過程を共有します。
システム構成
- フロントエンド: Next.js (App Router)
- バックエンド: Firebase Functions
- PDF処理: pdf.js
- CORS対応: cors middleware
Firebase Functions の実装
1. プロジェクトの初期設定
まず、Firebase Functionsのプロジェクト構成を設定します。
{ "name": "functions", "description": "Cloud Functions for Firebase", "type": "module", "scripts": { "serve": "firebase emulators:start --only functions", "deploy": "firebase deploy --only functions" }, "engines": { "node": "16" }, "dependencies": { "firebase-admin": "^9.8.0", "firebase-functions": "^3.14.1", "pdfjs-dist": "^3.11.174", "cors": "^2.8.5" } }
2. PDF処理関数の実装
Firebase Functionsでのメイン処理を実装します。
import { https } from "firebase-functions"; import * as pdfjsLib from "pdfjs-dist"; import cors from "cors"; import admin from "firebase-admin"; admin.initializeApp(); const corsHandler = cors({ origin: "*", methods: ["POST", "OPTIONS"], }); export const extractpdftext = https.onRequest((request, response) => { return corsHandler(request, response, async () => { try { if (request.method !== "POST") { response.status(405).send("Method Not Allowed"); return; } const pdfBuffer = request.body.pdfBuffer; if (!pdfBuffer) { response.status(400).send("PDF data is required"); return; } // PDFからテキストを抽出 const pdf = await pdfjsLib.getDocument(pdfBuffer).promise; let fullText = ""; for (let i = 1; i <= pdf.numPages; i++) { const page = await pdf.getPage(i); const textContent = await page.getTextContent(); const pageText = textContent.items.map((item) => item.str).join(" "); fullText += pageText + "\n"; } response.json({ text: fullText }); } catch (error) { console.error("PDF処理エラー:", error); response.status(500).send("PDF処理に失敗しました"); } }); });
実装のポイント
1. CORSの設定
クロスオリジンリクエストを適切に処理するため、corsミドルウェアを使用しています:
const corsHandler = cors({ origin: "*", methods: ["POST", "OPTIONS"], });
2. エラーハンドリング
様々なエラーケースに対応するため、適切なエラーハンドリングを実装:
- メソッドチェック
- 必要なデータの存在確認
- PDF処理時のエラーキャッチ
3. PDFテキスト抽出
pdf.jsライブラリを使用して、PDFからテキストを抽出。
const pdf = await pdfjsLib.getDocument(pdfBuffer).promise; let fullText = ""; for (let i = 1; i <= pdf.numPages; i++) { const page = await pdf.getPage(i); const textContent = await page.getTextContent(); const pageText = textContent.items.map((item) => item.str).join(" "); fullText += pageText + "\n"; }
実装で苦労した点
pdf.jsの設定
- Node.js環境でのpdf.jsの設定が特殊
- ブラウザ環境とは異なる使用方法への対応
CORSの適切な設定
- 開発環境と本番環境での動作の違い
- OPTIONSリクエストへの対応
エラーハンドリング
- 様々なエラーケースへの対応
- クライアントへの適切なエラーメッセージの返却
セキュリティ考慮事項
- ファイルサイズ制限
- 大きすぎるPDFファイルによるメモリ消費の防止
- アップロード制限の設定
- CORS設定
- 本番環境では適切なオリジン制限が必要
- origin: "*" は開発用
- エラー情報の制限
- 詳細なエラー情報は内部でログ
- クライアントには必要最小限の情報のみ返却
デプロイと運用
- デプロイ手順
cd functions
npm install
firebase deploy --only functions
- 環境変数の管理
- Firebase Console での環境変数設定
- 開発環境と本番環境の分離
今後の改善点
- パフォーマンス最適化
- PDFテキスト抽出処理の効率化
- メモリ使用量の最適化
2.機能拡張
- OCR機能の追加
- 画像付きPDFへの対応
- テキストフォーマットの改善
- エラーハンドリングの強化
- より詳細なエラーメッセージ
- リトライメカニズムの実装
まとめ
Firebase Functionsを使用したPDF解析機能の実装は、いくつかの技術的課題がありましたが、最終的に安定した機能を提供することができました。特にCORSの設定やPDFファイルの処理については、サーバーレス環境での実装における重要な学びとなりました。この実装を通じて、Firebase Functionsでのファイル処理パターンやエラーハンドリングの基本的な方法を理解することができ、今後の同様の実装に活かせる貴重な経験となりました。