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

Cloud Vision OCR词数统计异常及Firestore重复写入问题咨询

问题分析与解决方案

核心问题原因

1. Firestore 多次写入

Firebase Storage 的 object.finalize 触发器可能因重试机制、元数据更新或重复事件,导致云函数针对同一 JSON 文件多次执行。代码中未添加幂等性校验,每次触发都会向 Firestore 写入数据。

2. 词数统计结果不一致

  • 文本拼接错误:使用 readyArray.join() 默认以逗号拼接各页文本,会导致跨页面的单词被合并(如“page1end,page2start”会被视为单个单词),干扰词数统计。
  • 潜在的文件未完全写入:若云函数在 JSON 文件未完全上传至 Storage 时触发,下载到的内容不完整,导致文本提取和词数统计结果不稳定。
  • 冗余异步包装:在 map 中使用不必要的 async 函数处理同步数据,虽不直接引发问题,但可能导致逻辑混乱。
  • 词数统计逻辑局限性:仅通过 /\s+/ 分割文本,未处理标点符号与单词粘连的情况(如“hello,”会被计为一个单词),若 OCR 输出标点格式不稳定,也会影响统计结果。

解决方案

1. 确保 Firestore 单次写入(幂等性处理)

添加幂等校验,仅当 Firestore 文档未存在有效数据时才执行写入:

// 写入前检查文档状态
const docSnapshot = await docRef.get();
if (!docSnapshot.exists || !docSnapshot.data().wordCount) {
  await docRef.set({
    fullText: fullTextReady,
    textConfidence: textConfidence3,         
    type: "application/pdf",
    pageCount: jsObject.responses.length,
    languages: languages,
    fileName: fileName,
    location: jsonLocation,
    wordCount: words
  }, { merge: true });
}

或通过 Storage 文件元数据标记已处理状态,避免重复执行:

const file = bucket.file(filePath);
const [metadata] = await file.getMetadata();
if (metadata.metadata?.processed === 'true') {
  return; // 已处理过,直接终止函数
}

// 处理完成后标记元数据
await file.setMetadata({
  metadata: { processed: 'true' }
});

2. 修正词数统计逻辑

  • 正确拼接文本:用空格拼接各页文本,保留单词边界:
    const fullTextReady = readyArray.join(' ');
    
  • 移除冗余异步代码:处理同步数据时无需 async 包装,并添加空值 fallback:
    const textArray = jsObject.responses.map(response => {
      return response.fullTextAnnotation.text || '';
    });
    const readyArray = textArray; // 无需Promise.all,直接使用
    
  • 优化词数统计函数:处理空文本情况,可选添加标点清理逻辑:
    function countWords(str) {
      if (!str.trim()) return 0;
      // 可选:移除标点后再统计(根据需求调整)
      const cleanedStr = str.replace(/[^\w\s]/g, '');
      return cleanedStr.trim().split(/\s+/).length;
    }
    const words = countWords(fullTextReady); // 无需await,直接调用
    
  • 确保文件完整下载:添加错误捕获,避免解析不完整的 JSON:
    let jsObject;
    try {
      const downloadResponse = await bucket.file(filePath).download();
      const bufferToJson = downloadResponse.toString();
      jsObject = JSON.parse(bufferToJson);
    } catch (err) {
      console.error('文件下载或解析失败:', err);
      return; // 终止执行,避免无效写入
    }
    

3. 其他代码优化

移除不必要的 Promise.all 调用(处理同步数组时无需):

// 原代码中pages是同步数组,无需Promise.all
const pages = jsObject.responses.map(response => {
  return response.fullTextAnnotation.pages.map(page => {
    return page.property.detectedLanguages;
  });
});
const detectedLanguages = pages.flat(2);

// languages数组是同步处理的,无需Promise.all
const languages = detectedLanguages.map(language => {
  const langCode = language.languageCode;
  const confidence = Number((language.confidence).toFixed(1)) * 100;
  return {
    languageCode: langCode,
    languageConfidence: confidence
  };
});

内容的提问来源于stack exchange,提问作者DonBergen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 23:28:11