【问题标题】:Microsoft Graph API file upload using PHP SDK for large file still fails使用 PHP SDK 为大文件上传 Microsoft Graph API 文件仍然失败
【发布时间】:2019-10-28 20:01:36
【问题描述】:

我目前正在使用microsoft-php-sdk,它已经相当不错了。我设法将小文件从服务器上传到 OneDrive。但是当我尝试上传一个 38MB 的 Powerpoint 文件时,它失败了。 Microsoft Graph API 文档建议创建一个上传会话。我认为这就像将 URI 从 /content 更新为 /createUploadSession 一样简单,但它仍然失败。

$response = $graph->createRequest('POST', '/me/drive/root/children/'.basename($path).'/createUploadSession')
      ->setReturnType(Model\DriveItem::class)
      ->upload($path);

我的代码看起来像这样。我很难弄清楚 PHP SDK 文档,并且没有上传会话的示例。以前有没有人在这种情况下使用过 PHP SDK?

【问题讨论】:

    标签: php upload microsoft-graph-api onedrive


    【解决方案1】:

    我在 https://github.com/microsoftgraph/msgraph-sdk-php/wiki 和 laravel 框架中使用了类似的方法。这是最终对我有用的代码。

    public function test()
    {
        //initialization
        $viewData = $this->loadViewData();
    
        // Get the access token from the cache
    $tokenCache = new TokenCache();
    $accessToken = $tokenCache->getAccessToken();
    
    // Create a Graph client
    $graph = new Graph();
    $graph->setAccessToken($accessToken);
    //upload larger files
    // 1. create upload session
            $fileLocation = 'S:\ebooks\myLargeEbook.pdf';
    
    
             $file =  \File::get($fileLocation);
                $reqBody=array(
        "@microsoft.graph.conflictBehavior"=> "rename | fail | replace",
        "description"=> "description",
        "fileSystemInfo"=> ["@odata.type"=> "microsoft.graph.fileSystemInfo"]  ,
            "name"=> "ebook.pdf",
          );
            $uploadsession=$graph->createRequest("POST", "/drive/root:/test/ebook.pdf:/createUploadSession")
            ->attachBody($reqBody)
            ->setReturnType(Model\UploadSession::class)
            ->execute();
    //2. upload bytes
            $fragSize =320 * 1024;// 1024 * 1024 * 4;
            $fileLocation = 'S:\ebooks\myLargeEbook.pdf';
    // check if exists file if not make one
        if (\File::exists($fileLocation)) {
    $graph_url = $uploadsession->getUploadUrl();
            $fileSize = filesize($fileLocation);
    $numFragments = ceil($fileSize / $fragSize);
            $bytesRemaining = $fileSize;
            $i = 0;
    while ($i < $numFragments) {
                $chunkSize = $numBytes = $fragSize;
                $start = $i * $fragSize;
                $end = $i * $fragSize + $chunkSize - 1;
                $offset = $i * $fragSize;
                if ($bytesRemaining < $chunkSize) {
                    $chunkSize = $numBytes = $bytesRemaining;
                    $end = $fileSize - 1;
                }
                if ($stream = fopen($fileLocation, 'r')) {
                    // get contents using offset
                    $data = stream_get_contents($stream, $chunkSize, $offset);
                    fclose($stream);
                }
    $content_range = "bytes " . $start . "-" . $end . "/" . $fileSize;
    $headers = array(
                    "Content-Length"=> $numBytes,
                    "Content-Range"=> $content_range
                );
    $uploadByte = $graph->createRequest("PUT", $graph_url)
                ->addHeaders($headers)
                ->attachBody($data)
                    ->setReturnType(Model\UploadSession::class)
                    ->setTimeout("1000")
                    ->execute();
    $bytesRemaining = $bytesRemaining - $chunkSize;
                $i++;
            }
    
        }
        dd($uploadByte);
               }
    }
    

    【讨论】:

      【解决方案2】:

      我基于 microsoft graph rest api 为 oneDrive 开发了一个类似的库。这个问题在这里也解决了:tarask/oneDrive

      查看上传大文件部分中的文档

      【讨论】:

        【解决方案3】:

        我不熟悉 PHP,但我熟悉上传 API。希望这会有所帮助。

        您之前使用的/content 端点允许您将二进制内容直接写入文件并按照代码的预期返回DriveItem。 /createUploadSession 方法的工作方式不同。 resumable upload 的 Graph 文档对此进行了详细说明,但我将在此处进行总结。

        1. 不是在 CreateUploadSession 请求中发送二进制内容,而是发送一个空正文,或者发送一个 JSON 有效负载,其中包含文件名或冲突解决行为等元数据。
        2. 来自 CreateUploadSession 的响应是 UploadSession 对象,而不是 DriveItem。该对象有一个uploadUrl 属性,可用于发送二进制数据。
        3. 使用 HTTP Content-Range 标头通过多个请求上传二进制数据,以指示您要上传的字节范围。
        4. 一旦服务器接收到文件的最后一个字节,上传就会自动完成。

        虽然此概述说明了基础知识,但您还应围绕一些概念进行编码。例如,如果您的一个字节范围上传失败,您需要询问服务器它已经拥有哪些字节范围以及从哪里恢复。文档中详细说明了这一点和其他内容。 https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/driveitem_createuploadsession

        【讨论】:

          【解决方案4】:
          <?php
          require __DIR__.'/path/to/vendor/autoload.php';
          
          use Microsoft\Graph\Graph;
          use Microsoft\Graph\Model;
          
          $graph = new Graph();
          $graph->setAccessToken('YOUR_TOKEN_HERE');
          
          /** @var Model\UploadSession $uploadSession */
          $uploadSession = $graph->createRequest("POST", "/me/drive/items/root:/doc-test2.docx:/createUploadSession")
              ->addHeaders(["Content-Type" => "application/json"])
              ->attachBody([
                  "item" => [
                      "@microsoft.graph.conflictBehavior" => "rename",
                      "description"    => 'File description here'
                  ]
              ])
              ->setReturnType(Model\UploadSession::class)
              ->execute();
          
          $file = __DIR__.'/path/to/large-file.avi';
          $handle = fopen($file, 'r');
          $fileSize = fileSize($file);
          $fileNbByte = $fileSize - 1;
          $chunkSize = 1024*1024*4;
          $fgetsLength = $chunkSize + 1;
          $start = 0;
          while (!feof($handle)) {
              $bytes = fread($handle, $fgetsLength);
              $end = $chunkSize + $start;
              if ($end > $fileNbByte) {
                  $end = $fileNbByte;
              }
              /* or use stream
              $stream = \GuzzleHttp\Psr7\stream_for($bytes);
              */
              $res = $graph->createRequest("PUT", $uploadSession->getUploadUrl())
                  ->addHeaders([
                      'Content-Length' => ($end - 1) - $start,
                      'Content-Range' => "bytes " . $start . "-" . $end . "/" . $fileSize
                  ])
                  ->setReturnType(Model\UploadSession::class)
                  ->attachBody($bytes)
                  ->execute();
          
              $start = $end + 1;
          }
          

          这对我有用!

          【讨论】:

          • 欢迎来到 Stack Overflow!感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。 proper explanation would greatly improve its long-term value 通过展示为什么这是一个很好的解决问题的方法,并将使其对未来有其他类似问题的读者更有用。请edit您的回答添加一些解释,包括您所做的假设。
          • 但是我没有看到你在任何地方使用$stream 变量,那么它有什么用呢?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-26
          • 2021-10-14
          • 2011-05-04
          • 1970-01-01
          • 1970-01-01
          • 2012-04-12
          相关资源
          最近更新 更多