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

PHP实现OneDrive大文件上传遇阻:SDK及Curl方案均报错

解决OneDrive大文件上传的两种方案报错问题

一、Curl方案「API not found」错误修复

问题原因

  1. 端点混用错误:创建上传会话用了旧的https://api.onedrive.com/v1.0端点,上传小文件又切换到Graph端点,且小文件上传的请求方法错误。
  2. 接口使用不当:上传小于4MB的文件需用PUT方法,而非POST。

修复后的代码

1. 创建大文件上传会话

$createSessionUrl = "https://graph.microsoft.com/v1.0/me/drive/root:/$fileName:/createUploadSession";

$headers = [
    "Authorization: Bearer $accessToken",
    "Content-Type: application/json"
];

$postData = json_encode([
    'item' => [
        '@microsoft.graph.conflictBehavior' => 'rename',
        'name' => $fileName
    ]
]);

$ch = curl_init($createSessionUrl);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$responseArray = json_decode($response, true);
if ($httpCode >= 200 && $httpCode < 300) {
    return $responseArray;
} else {
    echo "Failed to create upload session: HTTP $httpCode - " . json_encode($responseArray);
    return null;
}

2. 小文件上传

$uploadUrl = "https://graph.microsoft.com/v1.0/me/drive/root:/$fileName:/content";
$fileContent = file_get_contents($filePath);

$headers = [
    "Authorization: Bearer $accessToken",
    "Content-Type: application/octet-stream"
];

$ch = curl_init($uploadUrl);
curl_setopt($ch, CURLOPT_PUT, true); // 改用PUT方法
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fileContent);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

$responseArray = json_decode($response, true);
if ($httpCode >= 200 && $httpCode < 300) {
    echo "File uploaded successfully";
} else {
    echo "Failed to upload small file: HTTP $httpCode - " . json_encode($responseArray);
}

关键注意事项

  • 统一使用Microsoft Graph端点https://graph.microsoft.com/v1.0,废弃旧OneDrive独立端点。
  • accessToken需通过授权码流程获取(个人OneDrive),并拥有Files.ReadWrite或Files.ReadWrite.All权限,客户端凭证流程无法访问/me端点。

二、Graph SDK方案「invalid_grant」错误修复

问题原因

  1. 认证上下文不匹配:用客户端凭证流程获取token,却用AuthorizationCodeContext初始化客户端,且尝试访问me()->drive(),但客户端凭证流程无当前用户,无法使用/me。
  2. 流程与场景不匹配:客户端凭证流程仅适用于企业/教育版OneDrive,个人OneDrive必须用授权码流程。

修复后的代码

情况1:个人OneDrive(授权码流程)

require_once 'vendor/autoload.php';

use GuzzleHttp\Client;
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Kiota\Authentication\Oauth\AuthorizationCodeContext;
use Microsoft\Graph\Core\Tasks\LargeFileUploadTask;
use Microsoft\Graph\Generated\Drives\Item\Items\Item\CreateUploadSession\CreateUploadSessionPostRequestBody as DriveItemCreateUploadSessionPostRequestBody;
use Microsoft\Graph\Generated\Models;
use GuzzleHttp\Psr7\Utils;

$clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx4";
$clientSecret = "xxxxxxxxxxxxxxxxxxraqR";
$tenantId = "common";
$redirectUri = "http://localhost/test1/src/test3/test.php";

// 先通过授权码地址获取code:
// https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=$clientId&response_type=code&redirect_uri=$redirectUri&scope=Files.ReadWrite.All offline_access

// 假设已获取授权码$code,交换token
$guzzle = new Client();
$tokenUrl = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token";
$tokenResponse = $guzzle->post($tokenUrl, [
    'form_params' => [
        'client_id' => $clientId,
        'client_secret' => $clientSecret,
        'code' => $code,
        'redirect_uri' => $redirectUri,
        'grant_type' => 'authorization_code'
    ]
]);
$token = json_decode($tokenResponse->getBody()->getContents(), true);

// 初始化客户端
$tokenContext = new AuthorizationCodeContext(
    $tenantId,
    $clientId,
    $clientSecret,
    $token['access_token'],
    $redirectUri,
    $token['refresh_token']
);
$graphClient = new GraphServiceClient($tokenContext, ['Files.ReadWrite.All']);

function uploadFileToOneDrive(GraphServiceClient $graphClient, string $filePath, string $itemPath): void {
    $file = Utils::streamFor(fopen($filePath, 'r'));

    $uploadProperties = new Models\DriveItemUploadableProperties();
    $uploadProperties->setAdditionalData([
        '@microsoft.graph.conflictBehavior' => 'replace'
    ]);

    $uploadSessionRequest = new DriveItemCreateUploadSessionPostRequestBody();
    $uploadSessionRequest->setItem($uploadProperties);

    $uploadSession = $graphClient->me()->drive()->root()->itemWithPath($itemPath)->createUploadSession()->post($uploadSessionRequest)->wait();

    $largeFileUpload = new LargeFileUploadTask($uploadSession, $graphClient->getRequestAdapter(), $file);
    $totalSize = $file->getSize();
    $progress = fn($prog) => print('Uploaded '.$prog[1].' of '.$totalSize.' bytes'.PHP_EOL);

    try {
        $largeFileUpload->upload($progress)->wait();
        echo "File uploaded successfully";
    } catch (\Psr\Http\Client\NetworkExceptionInterface $ex) {
        $largeFileUpload->resume()->wait();
    }
}

uploadFileToOneDrive($graphClient, "test.txt", "test.txt");

情况2:企业/教育版OneDrive(客户端凭证流程)

require_once 'vendor/autoload.php';

use Microsoft\Graph\GraphServiceClient;
use Microsoft\Kiota\Authentication\Oauth\ClientCredentialContext;
use Microsoft\Graph\Core\Tasks\LargeFileUploadTask;
use Microsoft\Graph\Generated\Drives\Item\Items\Item\CreateUploadSession\CreateUploadSessionPostRequestBody as DriveItemCreateUploadSessionPostRequestBody;
use Microsoft\Graph\Generated\Models;
use GuzzleHttp\Psr7\Utils;

$clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx4";
$clientSecret = "xxxxxxxxxxxxxxxxxxraqR";
$tenantId = "your-tenant-id"; // 替换为实际租户ID,不能用common
$driveId = "your-site-drive-id"; // 替换为目标站点的Drive ID

// 初始化客户端凭证上下文
$tokenContext = new ClientCredentialContext(
    $tenantId,
    $clientId,
    $clientSecret
);
$graphClient = new GraphServiceClient($tokenContext, ['Files.ReadWrite.All']);

function uploadFileToOneDrive(GraphServiceClient $graphClient, string $filePath, string $itemPath, string $driveId): void {
    $file = Utils::streamFor(fopen($filePath, 'r'));

    $uploadProperties = new Models\DriveItemUploadableProperties();
    $uploadProperties->setAdditionalData([
        '@microsoft.graph.conflictBehavior' => 'replace'
    ]);

    $uploadSessionRequest = new DriveItemCreateUploadSessionPostRequestBody();
    $uploadSessionRequest->setItem($uploadProperties);

    $uploadSession = $graphClient->drives()->byDriveId($driveId)->root()->itemWithPath($itemPath)->createUploadSession()->post($uploadSessionRequest)->wait();

    $largeFileUpload = new LargeFileUploadTask($uploadSession, $graphClient->getRequestAdapter(), $file);
    $totalSize = $file->getSize();
    $progress = fn($prog) => print('Uploaded '.$prog[1].' of '.$totalSize.' bytes'.PHP_EOL);

    try {
        $largeFileUpload->upload($progress)->wait();
        echo "File uploaded successfully";
    } catch (\Psr\Http\Client\NetworkExceptionInterface $ex) {
        $largeFileUpload->resume()->wait();
    }
}

uploadFileToOneDrive($graphClient, "test.txt", "test.txt", $driveId);

关键注意事项

  • 个人OneDrive必须用授权码流程,客户端凭证流程仅支持企业/教育版站点Drive。
  • Azure AD应用需配置对应权限:个人场景添加Files.ReadWrite.All并完成用户同意;企业场景需管理员同意。
  • 认证上下文要与token流程匹配:授权码用AuthorizationCodeContext,客户端凭证用ClientCredentialContext。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 11:27:03