How do I send the final bytes for a Microsoft Graph Upload Session?

Viewed 279

I am following the document provided by Microsoft for Uploading Large files into Microsoft Graph.
Everything works great except for the byte array on the final bytes of the file.

https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0

First I create a DriveItem that has a file name and upload an empty byte[].

using (var emptyFileClient = new HttpClient())
{
   var url = "https://graph.microsoft.com/v1.0/drives/" + driveID + "/root:/" + sharepoint2013ID + "/" + fileInfo.Name + ":/content";
   emptyFileClient.DefaultRequestHeaders.Add("Authorization", bearer);
   var emptyFileContent = new ByteArrayContent(new byte[0]);
                
   var emptyFileResponse = emptyFileClient.PutAsync(url, emptyFileContent).Result;
   if (emptyFileResponse.StatusCode != System.Net.HttpStatusCode.Created)
   {
      return false;
   }
}

Then I make sure that I have a valid DriveItemID

var client = new RestClient("https://graph.microsoft.com/v1.0/drives/" + driveID + "/root:/" + sharepoint2013ID + "/" + fileInfo.Name);
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", bearer);
IRestResponse response = client.Execute(request);
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
    return null;
}

Next I create an UploadSession

//Create UploadSession
var client = new RestClient("https://graph.microsoft.com/v1.0/drives/" + driveID + "/items/" + driveItem.id + "/createUploadSession");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", bearer);
IRestResponse response = client.Execute(request);
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
   throw new Exception("Could Not Create Upload Session");
}

This works correctly so now I begin uploading the file. Again, Everything works correctly until the final byte[].

I have verified that the expected range for the final byte[] matches the byte array that I send.

WebRequest request = null;
using (Stream source = File.OpenRead(fileInfo.FullName))
{
    double fileSizeBytes = fileInfo.Length;
    int bufferSize = 2048000;
    byte[] buffer = new byte[bufferSize];
    int bytesRead;
    var loopCount = 0;
    while ((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0)
    {
        double startByte = bufferSize * loopCount;
        double endByte = startByte + bytesRead - 1;
        var contentRange = "bytes " + startByte + "-" + endByte + "/" + fileSizeBytes;
        Console.WriteLine(contentRange);

        request = WebRequest.Create(uploadSession.uploadUrl);
        request.Headers.Add("Authorization", bearer);
        request.Headers.Add("Content-Length", bytesRead.ToString());
        request.Headers.Add("Content-Range", contentRange);
        request.Method = "PUT";
        request.ContentLength = buffer.Length;
        Stream dataStream = request.GetRequestStream();
        dataStream.Write(buffer, 0, buffer.Length);
        dataStream.Close();
                    
        var response = (HttpWebResponse)request.GetResponse();
        if (response.StatusCode != System.Net.HttpStatusCode.Accepted)
        {
            return false;
        }
        loopCount++;
    }
}

I have been testing with large files that exceed the buffer size as to split the upload into multiple requests. You can also test with smaller files if you reduce the buffer size. Any help on this would be appreciated.

Also I am not interested in using the Graph.SDK or any other library, this needs to run from web requests. This is for a hybrid solution where Sharepoint2013 Server will be saving some files in SharepointOnline.

1 Answers

The issue turned out to be that I was setting the content-length header twice and one of them was using the incorrect value. Simply comment out the following line and everything above will work correctly.

//request.ContentLength = buffer.Length;

On the final upload the length is not the full length of the buffer but instead needs to be set to bytesRead.

Related