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

使用Graph API批量创建OneDrive文件夹时创建1000个后停止的问题

问题

近期使用Microsoft Graph API实现功能:在共享文件夹中创建名称格式为uc_123455的目录,并在每个创建的文件夹内上传PDF文件。程序初始运行正常,但创建到1000个文件夹后便停止,总共需要创建约2323个文件夹。已尝试添加sleep或延长PHP执行时间,但均无效。

代码执行流程

  1. 从Microsoft获取access_token,初始化Graph实例并获取共享文件夹ID:
$this->graph = new Graph();
$this->graph->setAccessToken($this->accessToken);
$this->directory_id = $this->fetchDirectoryId($this->graph);
  1. 进入循环:
  • 根据循环阶段生成文件夹名称:
$subfolderName = "UC_" . $customer->numero_uc;
  • 调用createIntoDirectory函数创建文件夹:
$folder = $this->createIntoDirectory($this->directory_id[0], $this->graph, $subfolderName);

核心函数实现

createIntoDirectory函数

public function createIntoDirectory(string $folderId, Graph $graph, string $ucNumber): string|bool
{
    try {
        $response = $graph->createRequest('POST', "/me/drives/" . $this->drive_id . "/items/$folderId/children")
            ->attachBody(json_encode([
                "name" => $ucNumber,
                "folder" => new stdClass()
            ], JSON_THROW_ON_ERROR))
            ->setReturnType(DriveItem::class)
            ->execute();
        return $response->getId();
    } catch (GuzzleException|GraphException) {
        return $this->fetchExistenteDirectory($folderId, $graph, $ucNumber);
    }
}

fetchExistenteDirectory函数

public function fetchExistenteDirectory(string $folderId, Graph $graph, string $ucNumber): string|bool
{
    try {
        $folder = $graph->createRequest('GET', "/me/drives/" . $this->drive_id . "/items/$folderId/children")
            ->setReturnType(DriveItem::class)
            ->execute();
        foreach ($folder as $item) {
            if ($item->getName() === $ucNumber) {
                return $item->getId();
            }
        }
        return false;
    } catch (GuzzleException|GraphException) {
        return false;
    }
}

补充说明:

  • PDF文件:从数据库获取URL转换而来的图片生成的PDF。
  • UC:葡萄牙语"unidade consumidora"(消费单元)的缩写,每个UC对应数据库中的一条数据。

问题分析与解决方案

核心问题排查

  1. Graph API速率限制触发
    Microsoft Graph API对OneDrive操作有默认速率限制(通常为每10分钟1000次请求),创建1000个文件夹刚好触达阈值,后续请求被拒绝,导致程序停止。

  2. 文件夹存在性查询低效
    fetchExistenteDirectory函数每次查询会拉取目录下所有子项,随着文件夹数量增多,响应数据量暴增,既耗时又额外消耗请求配额,加速触发速率限制;同时未处理分页逻辑,当子项超过100个时,仅能获取第一页数据,可能导致找不到已存在文件夹。

  3. 异常处理不精准
    捕获异常后直接跳转查询逻辑,未区分速率限制(HTTP 429)等特定错误,导致无效请求浪费配额。

改进方案

  1. 处理速率限制重试
    在异常处理中检测HTTP 429错误,根据响应头Retry-After等待后重试:
catch (GuzzleException|GraphException $e) {
    if ($e instanceof ClientException && $e->getResponse()->getStatusCode() === 429) {
        $retryAfter = $e->getResponse()->getHeaderLine('Retry-After');
        sleep((int)$retryAfter ?: 10);
        return $this->createIntoDirectory($folderId, $graph, $ucNumber);
    }
    return $this->fetchExistenteDirectory($folderId, $graph, $ucNumber);
}
  1. 优化文件夹存在性查询
    使用Graph API的$filter和$select参数精准查询,减少数据传输量:
public function fetchExistenteDirectory(string $folderId, Graph $graph, string $ucNumber): string|bool
{
    try {
        $response = $graph->createRequest('GET', "/me/drives/" . $this->drive_id . "/items/$folderId/children?\$filter=name eq '$ucNumber' and folder ne null&\$select=id")
            ->setReturnType(DriveItem::class)
            ->execute();
        return !empty($response) ? $response[0]->getId() : false;
    } catch (GuzzleException|GraphException $e) {
        if ($e instanceof ClientException && $e->getResponse()->getStatusCode() === 429) {
            $retryAfter = $e->getResponse()->getHeaderLine('Retry-After');
            sleep((int)$retryAfter ?: 10);
            return $this->fetchExistenteDirectory($folderId, $graph, $ucNumber);
        }
        return false;
    }
}
  1. 任务分段执行
    将2323个文件夹创建任务拆分为多批次(如每500个一批),每批次执行完成后等待1-2分钟,避免短时间内请求过载。

  2. 检查access_token有效期
    access_token默认有效期1小时,若程序运行超时,需在循环中定期检测token状态,过期时重新获取。

内容的提问来源于stack exchange,提问作者Vinícius V. Beraldo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 01:55:11