0
votes

I am currently using microsoft-php-sdk and it has been pretty good. I have managed to upload small files from the server to OneDrive. But when I tried to upload a 38MB Powerpoint file it was failing. The Microsoft Graph API documentation suggests creating an upload session. I thought it would just be as easy as just updating the URI from /content to /createUploadSession but it was still failing.

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

My code looks something like this. I have difficulty figuring out the PHP SDK documentation and there was no example for upload session. Has anyone used the PHP SDK for this scenario before?

4

4 Answers

4
votes

i used the similar approach with https://github.com/microsoftgraph/msgraph-sdk-php/wiki and laravel framework. here is the code that worked for me finally.

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
votes

I have developed a similar library for oneDrive based on microsoft graph rest api. This problem is also solved here : tarask/oneDrive

look at the documentation in the Upload large files section

1
votes

I'm not familiar with PHP, but I am familiar with the upload API. Hopefully this will help.

The /content endpoint you were using before allows you to write binary contents to a file directly and returns a DriveItem as your code expects. The /createUploadSession method works differently. The Graph documentation for resumable upload details this, but I'll summarize here.

  1. Instead of sending the binary contents in the CreateUploadSession request, you either send an empty body or you send a JSON payload with metadata like the filename or conflict-resolution behavior.
  2. The response from CreateUploadSession is an UploadSession object, not a DriveItem. The object has an uploadUrl property that you use to send the binary data.
  3. Upload your binary data over multiple requests using the HTTP Content-Range header to indicate which byte range you're uploading.
  4. Once the server receives the last bytes of the file, the upload automatically finishes.

While this overview illustrates the basics, there are some more concepts you should code around. For example, if one of your byte ranges fails to upload, you need to ask the server which byte ranges it already has and where to resume. That and other things are detailed in the docs. https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/driveitem_createuploadsession

0
votes
<?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;
}

It's work for Me !