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

Node.js部署Azure Web App后下载Blob文件出现Base64/Buffer随机损坏问题咨询

问题根因
  • setTimeout逻辑完全错误。你是先读取临时文件得到不完整的base64后,才等待2秒再返回结果,等待操作并没有提前到读取文件之前,完全起不到等待文件写入完成的作用。
  • Azure Web App的./temp临时目录是网络挂载的共享存储,IO延迟远高于本地磁盘。旧版Azure Storage SDK的getBlobToLocalFile回调触发时,仅代表SDK侧的写入操作完成,网络存储侧可能还未完成数据同步,此时立即读取文件就会大概率读到不完整的内容。本地磁盘IO速度快,写入后立即读也能拿到完整数据,所以本地测试不会触发问题。
  • 重试逻辑存在缺陷,仅判断file是否为假值,不完整的base64属于合法字符串不会触发重试,直接返回给客户端。
  • 额外的本地文件读写环节引入了不必要的IO故障点,完全可以省略。
修复方案

最优方案:省略本地文件写入环节

直接调用getBlobToBuffer接口将Blob下载到内存,直接转base64,彻底规避磁盘IO问题:

storageUtils.downloadBlobFromCloud =  (subDirectory, fileName) => {
  if (!fileName) {
    return Promise.resolve(null);
  }
  return new Promise(  (resolve, reject) => {
    blobService.getBlobToBuffer(
      container,
      `${subDirectory}/${fileName}`,
      function (error, serverBlob, buffer) {
        if (error) {
          console.log("下载Blob失败", error)
          return reject(error);
        }
        const base64 = buffer.toString('base64')
        resolve(base64);
      }
    );
  });
};

如果你必须保留本地文件写入逻辑

将等待逻辑放在读取文件之前,同时校验下载的本地文件大小和Blob的实际大小是否一致:

storageUtils.downloadBlobFromCloud =  (subDirectory, fileName) => {
  var dir = './temp';
  // 加随机后缀避免同一毫秒多请求出现重名
  var tempFilePath = dir + '/' + Date.now().toString() + '-' + Math.random().toString(36).slice(2) + '.temp';
  console.log(tempFilePath)

  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir);
  }

  if (!fileName) {
    return Promise.resolve(null);
  }

  return new Promise(  (resolve, reject) => {
    blobService.getBlobToLocalFile(
      container,
      `${subDirectory}/${fileName}`,
      tempFilePath,
      async function (error, serverBlob) {
        if (error) {
          console.log("下载Blob失败", error)
          return reject(error);
        }
        try {
          // 等待网络存储同步完成
          await new Promise(r => setTimeout(r, 2000));
          // 校验文件大小和Blob实际大小是否一致,避免读到不完整文件
          const fileStat = fs.statSync(tempFilePath);
          if (fileStat.size !== serverBlob.contentLength) {
            throw new Error(`文件大小不匹配,预期${serverBlob.contentLength},实际${fileStat.size}`);
          }
          let base64 = await fileStream(tempFilePath);
          resolve(base64);
          // 延迟删除文件
          setTimeout(()=>{
            console.log('deleting file')
            fs.unlinkSync(tempFilePath);
          },10000);
        } catch (e) {
          console.log('处理文件失败', e)
          // 出错也清理临时文件
          fs.unlink(tempFilePath, ()=>{});
          reject(e);
        }
      }
    );
  });
};

完善重试逻辑

捕获处理失败的错误触发重试,不要设置过高的重试次数避免触发接口限流:

let downloadFile = async (directory, fileName, attempts) => {
  attempts++;
  try {
    let file = await storageUtils.downloadBlobFromCloud(directory, fileName);
    return file;
  } catch (e) {
    if(attempts < 3) {
      return await downloadFile(directory, fileName, attempts);
    }
    throw e;
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 12:48:00