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

如何通过While循环调用API直至Locator为空并分文件存储结果

修正后的Azure函数API循环调用实现

原代码存在的问题

  • 首次调用生成文件时未传入序号参数,会生成testundefined.csv
  • 循环调用时未更新locator参数,导致重复使用初始值,陷入死循环
  • 终止条件判断错误,API返回空locator时通常是null或空字符串,而非undefined
  • 数据数组lsttis未在每次循环后重置,导致每次写入的文件包含之前所有批次的数据
  • API域名前后不一致(首次调用是someapi.test.com,循环内是someapi.com)
  • 缺少错误捕获机制,API调用或文件写入失败会直接导致函数崩溃
  • 文件写入使用同步方法fs.writeFileSync,在异步函数中不符合最佳实践

修正后的完整代码

const fs = require('fs').promises;
const { parse } = require('json2csv');

const getJobResult = async (token) => {
  const baseUrl = "https://someapi.test.com/services/data/v59.0/jobs/query/750Pw000005PqqBIAS/results";
  const requestOptions = {
    method: "GET",
    headers: {
      "Content-Type": 'application/json',
      "Accept": "text/csv",
      "Authorization": `Bearer ${token}`
    },
    redirect: "follow"
  };

  // 首次调用API
  let locator;
  let fileIndex = 0;
  try {
    // 第一次请求
    const firstResp = await fetch(`${baseUrl}?maxRecords=500000`, requestOptions);
    if (!firstResp.ok) throw new Error(`首次请求失败: ${firstResp.status}`);
    
    const firstCsv = await firstResp.text();
    locator = firstResp.headers.get('locator');
    
    // 处理首次返回的数据并写入文件
    const firstRows = firstCsv.split(/\r?\n|\r/).slice(1).filter(row => row.trim()); // 过滤空行
    const firstData = firstRows.map(row => ({
      AccessLevel: 'Read',
      ParentId: row.replace(/['"]+/g, ''),
      RowCause: 'Manual',
      UserOrGroupId: '11111111'
    }));
    
    if (firstData.length > 0) {
      await createCsvFile(firstData, fileIndex);
      fileIndex++;
    }

    // 循环调用直到locator为空
    while (locator) {
      const loopResp = await fetch(`${baseUrl}?locator=${encodeURIComponent(locator)}&maxRecords=500000`, requestOptions);
      if (!loopResp.ok) throw new Error(`循环请求失败: ${loopResp.status}`);
      
      const loopCsv = await loopResp.text();
      const newLocator = loopResp.headers.get('locator');
      
      // 处理当前批次数据
      const loopRows = loopCsv.split(/\r?\n|\r/).slice(1).filter(row => row.trim());
      const loopData = loopRows.map(row => ({
        AccessLevel: 'Read',
        ParentId: row.replace(/['"]+/g, ''),
        RowCause: 'Manual',
        UserOrGroupId: '11111111'
      }));
      
      if (loopData.length > 0) {
        await createCsvFile(loopData, fileIndex);
        fileIndex++;
      }

      // 更新locator,准备下一次循环
      locator = newLocator;
    }
  } catch (error) {
    console.error('处理API请求或文件写入时出错:', error.message);
    throw error; // 抛出错误让Azure函数捕获处理
  }
};

const createCsvFile = async (data, index) => {
  const fields = ['AccessLevel', 'ParentId', 'RowCause', 'UserOrGroupId'];
  const csv = parse(data, { fields });
  await fs.writeFile(`./test${index}.csv`, csv);
};

// 导出函数供Azure函数调用
module.exports = getJobResult;

关键改进说明

  • 统一API域名:使用baseUrl变量确保所有请求域名一致
  • 正确更新locator:每次循环后更新locator值,避免死循环
  • 重置数据数组:每次请求单独处理当前批次数据,避免数据累积
  • 规范文件命名:首次调用使用fileIndex=0,后续递增生成test0.csv、test1.csv等
  • 错误处理:添加try/catch捕获请求和文件操作错误,避免函数崩溃
  • 异步文件写入:改用fs.promises.writeFile符合异步函数的最佳实践
  • 过滤空行:处理CSV数据时过滤空行,避免生成无效数据
  • URL编码:使用encodeURIComponent处理locator参数,防止特殊字符导致请求错误
  • 变量命名规范:使用驼峰命名法提升代码可读性

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 07:42:40