You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter应用后端PDF数据处理及Firebase存储方案咨询

解决方案:Flutter + Firebase + Python 实现司法PDF的存储与按需筛选

1. 先搞定PDF的元数据提取(Python端)

因为目标网站无API,你用Playwright下载PDF后,必须先提取每个PDF里的姓名和案件编号作为元数据——这是实现筛选的核心,不然没法从一堆PDF里快速定位目标文件。

  • 用pdfplumber或PyPDF2提取PDF文本(如果是扫描件,搭配pytesseract做OCR):
import pdfplumber
import re

def extract_metadata(pdf_path, year):
    metadata = {"year": year, "names": [], "case_numbers": []}
    with pdfplumber.open(pdf_path) as pdf:
        for page in pdf.pages:
            text = page.extract_text()
            if not text:
                continue
            # 正则匹配TRF5案件编号格式(可根据实际PDF调整)
            case_matches = re.findall(r"\d{7}-\d{2}\.\d{4}\.\d\.\d{2}\.\d{4}", text)
            metadata["case_numbers"].extend(case_matches)
            # 正则匹配姓名(假设姓名跟随"Nome:"标签,可根据PDF结构调整)
            name_matches = re.findall(r"Nome:\s*([A-Za-zÀ-ÿ\s]+)", text)
            metadata["names"].extend([name.strip() for name in name_matches])
    # 去重避免重复数据
    metadata["names"] = list(set(metadata["names"]))
    metadata["case_numbers"] = list(set(metadata["case_numbers"]))
    return metadata
  • 配合Playwright的下载逻辑,每下载完一个PDF就立即提取元数据,为后续上传Firebase做准备。

2. Firebase架构设计

2.1 Cloud Storage:存储原始PDF

  • 创建专属存储桶,按年份/类型分层存储,比如路径:2020/administrative/xxx.pdf,方便管理与权限控制。
  • 默认设置文件私有,通过后端生成临时下载链接,避免公开访问泄露数据。

2.2 Firestore:存储元数据索引

创建pdf_records集合,每个文档对应一个PDF,字段设计如下:

  • file_path:Cloud Storage中的文件路径(如2020/administrative/123.pdf)
  • year:文档所属年份(2020-2023)
  • orgao:固定值Seção Judiciária do Sergipe
  • edicao:固定值Administrativa
  • names:数组类型,存储提取到的所有姓名
  • case_numbers:数组类型,存储提取到的所有案件编号

通过Firestore的数组包含查询,就能快速定位匹配姓名或案件编号的PDF,无需逐个读取PDF内容。

3. 用Cloud Functions实现后端逻辑

3.1 批量上传函数(Python)

将下载并提取元数据的PDF上传到Cloud Storage,同时将元数据写入Firestore:

import firebase_admin
from firebase_admin import credentials, storage, firestore

# 初始化Firebase(需提前下载服务账号密钥)
cred = credentials.Certificate("service-account-key.json")
firebase_admin.initialize_app(cred, {
    "storageBucket": "your-bucket-name.appspot.com"
})
db = firestore.client()
bucket = storage.bucket()

def upload_pdf_to_firebase(pdf_local_path, metadata):
    # 上传PDF到Cloud Storage
    file_name = pdf_local_path.split("/")[-1]
    blob_path = f"{metadata['year']}/administrative/{file_name}"
    blob = bucket.blob(blob_path)
    blob.upload_from_filename(pdf_local_path)
    blob.content_type = "application/pdf"
    blob.patch()
    
    # 写入元数据到Firestore
    doc_ref = db.collection("pdf_records").document()
    doc_ref.set({
        "file_path": blob_path,
        "year": metadata["year"],
        "orgao": "Seção Judiciária do Sergipe",
        "edicao": "Administrativa",
        "names": metadata["names"],
        "case_numbers": metadata["case_numbers"]
    })

3.2 筛选与生成下载链接的函数(Node.js)

写一个Callable Function,接收前端的筛选参数,查询Firestore并生成临时下载链接:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

exports.getFilteredPdfs = functions.https.onCall(async (data, context) => {
    // 可选:验证用户身份(根据作业需求决定是否开启)
    if (!context.auth) {
        throw new functions.https.HttpsError("unauthenticated", "需验证身份");
    }

    const { name, caseNumber } = data;
    let query = admin.firestore().collection("pdf_records");
    
    // 按姓名筛选
    if (name) {
        query = query.where("names", "array-contains", name.trim());
    }
    // 按案件编号筛选
    if (caseNumber) {
        query = query.where("case_numbers", "array-contains", caseNumber.trim());
    }

    const snapshot = await query.get();
    const results = [];
    for (const doc of snapshot.docs) {
        const fileRef = admin.storage().bucket().file(doc.data().file_path);
        // 生成1小时有效的临时下载链接
        const [url] = await fileRef.getSignedUrl({
            action: "read",
            expires: Date.now() + 3600 * 1000
        });
        results.push({
            id: doc.id,
            year: doc.data().year,
            downloadUrl: url,
            names: doc.data().names,
            caseNumbers: doc.data().case_numbers
        });
    }
    return results;
});

4. Flutter前端集成

4.1 调用Cloud Functions获取筛选结果

用cloud_functions插件调用后端筛选接口:

import 'package:cloud_functions/cloud_functions.dart';

Future<List<dynamic>> fetchFilteredPdfs(String? name, String? caseNumber) async {
  final HttpsCallable callable = FirebaseFunctions.instance.httpsCallable('getFilteredPdfs');
  final results = await callable.call({
    'name': name,
    'caseNumber': caseNumber,
  });
  return results.data;
}

4.2 实现PDF下载

用flutter_downloader插件实现下载功能:

import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';

Future<void> downloadPdf(String url, String fileName) async {
  final directory = await getExternalStorageDirectory();
  await FlutterDownloader.enqueue(
    url: url,
    savedDir: directory!.path,
    fileName: fileName,
    showNotification: true,
    openFileFromNotification: true,
  );
}

关键注意事项

  • 如果PDF是扫描件(图片格式),需安装Tesseract引擎并搭配pytesseract做OCR文本提取。
  • 批量处理PDF时,建议分批次异步执行,避免单次任务超时。
  • Firestore的数组包含查询是精确匹配,提取姓名时需统一格式(如全大写/小写、去除多余空格)。
  • 调整Cloud Functions的内存与超时时间,确保处理大量查询时不会中断。

内容的提问来源于stack exchange,提问作者João Bosco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 10:47:02