none
Upload file (> 4MB) to OneDrive using Graph API RRS feed

  • Question

  • I am trying to upload files to OneDrive using Graph API. The below code works fine when I upload files with size lesser than 4MB but it shows error when I try to upload files more than 4 MB. I went through this documentation but still I am not sure how can I get this work.

    Below is my working code for files less than 4MB.

    using (var client = new HttpClient())
    {
        var url = "https://graph.microsoft.com/v1.0" + $"/drives/{driveID}/items/{folderId}:/{originalFileName}:/content";
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());
    
        byte[] sContents = System.IO.File.ReadAllBytes(filePath);
        var content = new ByteArrayContent(sContents);
    
        var response = client.PutAsync(url, content).Result.Content.ReadAsStringAsync().Result;
    }


    Please help


    - Mohamed Thaufeeq A

    Monday, May 27, 2019 9:15 AM

Answers

  • We need to split the byte array by length (i.e. 4MB) and pass them to the OneDrive API. The working version is below:

    using (var client = new HttpClient())
    {
        var url = "https://graph.microsoft.com/v1.0" + $"/drives/{driveID}/items/{folderId}:/{originalFileName}:/createUploadSession";
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());
    
        var sessionResponse = client.PostAsync(apiUrl, null).Result.Content.ReadAsStringAsync().Result;
    
        byte[] sContents = System.IO.File.ReadAllBytes(filePath);
        var uploadSession = JsonConvert.DeserializeObject<UploadSessionResponse>(sessionResponse);
        string response = UploadFileBySession(uploadSession.uploadUrl, sContents);
    }

    UploadFileBySession is as follows:

    private string UploadFileBySession(string url, byte[] file)
    {
        int fragSize = 1024 * 1024 * 4;
        var arrayBatches = ByteArrayIntoBatches(file, fragSize);
        int start = 0;
        string response = "";
    
        foreach (var byteArray in arrayBatches)
        {
            int byteArrayLength = byteArray.Length;
            var contentRange = " bytes " + start + "-" + (start + (byteArrayLength - 1)) + "/" + file.Length;
    
            using (var client = new HttpClient())
            {
                var content = new ByteArrayContent(byteArray);
                content.Headers.Add("Content-Length", byteArrayLength.ToProperString());
                content.Headers.Add("Content-Range", contentRange);
    
                response = client.PutAsync(url, content).Result.Content.ReadAsStringAsync().Result;
            }
    
            start = start + byteArrayLength;
        }
        return response;
    }
    
    internal IEnumerable<byte[]> ByteArrayIntoBatches(byte[] bArray, int intBufforLengt)
    {
        int bArrayLenght = bArray.Length;
        byte[] bReturn = null;
    
        int i = 0;
        for (; bArrayLenght > (i + 1) * intBufforLengt; i++)
        {
            bReturn = new byte[intBufforLengt];
            Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLengt);
            yield return bReturn;
        }
    
        int intBufforLeft = bArrayLenght - i * intBufforLengt;
        if (intBufforLeft > 0)
        {
            bReturn = new byte[intBufforLeft];
            Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLeft);
            yield return bReturn;
        }
    }

    UploadSessionResponse class file to deserialize JSON response when you create upload session

    public class UploadSessionResponse
    {
        public string odatacontext { get; set; }
        public DateTime expirationDateTime { get; set; }
        public string[] nextExpectedRanges { get; set; }
        public string uploadUrl { get; set; }
    }




    Thursday, May 30, 2019 10:18 AM

All replies

  • This is the error I am getting :

    {

      "error": {
        "code": "BadRequest",
        "message": "Maximum request length exceeded.",
        "innerError": {
          "request-id": "d7ccccee-d101-4660-97cd-f574d30256e0",
          "date": "2019-05-27T06:29:25"
        }
      }
    }


    - Mohamed Thaufeeq A

    Monday, May 27, 2019 9:16 AM
  • Hi Mohamed,

    Thank you for posting here.

    Based on my search, I find onedrive will have some limits in some cases. You could look at the following link.

    https://docs.microsoft.com/en-us/onedrive/developer/rest-api/concepts/upload?view=odsp-graph-online

    Best Regards,

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, May 28, 2019 2:22 AM
    Moderator
  • Thank you Jack,

    I have mentioned this link in the question itself. I know we need to create resumable upload through multiple session to upload files with size more than 4MB. 

    I would like to know how can I do below?

    PUT https://sn3302.up.1drv.com/up/fe6987415ace7X4e1eF866337

    Content-Length: 26 Content-Range: bytes 0-25/128 <bytes 0-25 of the file>

    through this code

    using (var client = new HttpClient())
    {
        var url = "https://graph.microsoft.com/v1.0" + $"/drives/{driveID}/items/{folderId}:/{originalFileName}:/content";
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());
    
        byte[] sContents = System.IO.File.ReadAllBytes(filePath);
        var content = new ByteArrayContent(sContents);
    
        var response = client.PutAsync(url, content).Result.Content.ReadAsStringAsync().Result;
    }


    - Mohamed Thaufeeq A

    Tuesday, May 28, 2019 5:29 AM
  • Hi

    Thanks for the feedback.

    Based on my search, I could not find the solution to your problem. In some cases, it may be not related to c#, it is more related to onedrive. Therefore I suggest that you could ask the question in the following link.

    https://techcommunity.microsoft.com/t5/OneDrive-for-Business/ct-p/OneDriveforBusiness

    Best Regards

    Jack


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, May 28, 2019 6:59 AM
    Moderator
  • To upload files up to 60 MB in size you need to first create an upload session. Then upload the file in correct size chunks. In your case you're simply calling the standard Upload method. You'll be making multiple calls now.

    However I might recommend that you forego this complexity and first try using an existing client that should handle this for you. You don't need to write all this (error prone) code yourself.

    Here's a client from the OneDrive team that you can import into your project.

    If that doesn't work then CloudRail has their own version.

    In either case go to your project, Add a NuGet package and then point it to one of the clients. Then use the provided client to call OneDrive for you.


    Michael Taylor http://www.michaeltaylorp3.net

    Tuesday, May 28, 2019 2:08 PM
    Moderator
  • We need to split the byte array by length (i.e. 4MB) and pass them to the OneDrive API. The working version is below:

    using (var client = new HttpClient())
    {
        var url = "https://graph.microsoft.com/v1.0" + $"/drives/{driveID}/items/{folderId}:/{originalFileName}:/createUploadSession";
        client.DefaultRequestHeaders.Add("Authorization", "Bearer " + GetAccessToken());
    
        var sessionResponse = client.PostAsync(apiUrl, null).Result.Content.ReadAsStringAsync().Result;
    
        byte[] sContents = System.IO.File.ReadAllBytes(filePath);
        var uploadSession = JsonConvert.DeserializeObject<UploadSessionResponse>(sessionResponse);
        string response = UploadFileBySession(uploadSession.uploadUrl, sContents);
    }

    UploadFileBySession is as follows:

    private string UploadFileBySession(string url, byte[] file)
    {
        int fragSize = 1024 * 1024 * 4;
        var arrayBatches = ByteArrayIntoBatches(file, fragSize);
        int start = 0;
        string response = "";
    
        foreach (var byteArray in arrayBatches)
        {
            int byteArrayLength = byteArray.Length;
            var contentRange = " bytes " + start + "-" + (start + (byteArrayLength - 1)) + "/" + file.Length;
    
            using (var client = new HttpClient())
            {
                var content = new ByteArrayContent(byteArray);
                content.Headers.Add("Content-Length", byteArrayLength.ToProperString());
                content.Headers.Add("Content-Range", contentRange);
    
                response = client.PutAsync(url, content).Result.Content.ReadAsStringAsync().Result;
            }
    
            start = start + byteArrayLength;
        }
        return response;
    }
    
    internal IEnumerable<byte[]> ByteArrayIntoBatches(byte[] bArray, int intBufforLengt)
    {
        int bArrayLenght = bArray.Length;
        byte[] bReturn = null;
    
        int i = 0;
        for (; bArrayLenght > (i + 1) * intBufforLengt; i++)
        {
            bReturn = new byte[intBufforLengt];
            Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLengt);
            yield return bReturn;
        }
    
        int intBufforLeft = bArrayLenght - i * intBufforLengt;
        if (intBufforLeft > 0)
        {
            bReturn = new byte[intBufforLeft];
            Array.Copy(bArray, i * intBufforLengt, bReturn, 0, intBufforLeft);
            yield return bReturn;
        }
    }

    UploadSessionResponse class file to deserialize JSON response when you create upload session

    public class UploadSessionResponse
    {
        public string odatacontext { get; set; }
        public DateTime expirationDateTime { get; set; }
        public string[] nextExpectedRanges { get; set; }
        public string uploadUrl { get; set; }
    }




    Thursday, May 30, 2019 10:18 AM
  • Hello Bro,

    In the above code how  you will generate the  accesstoken from GetAccessToken() method

    Friday, April 17, 2020 3:13 PM
  • Please do not respond to old questions and ask new ones. Create your own post and, optionally, link to the original question.

    Michael Taylor http://www.michaeltaylorp3.net

    Friday, April 17, 2020 3:44 PM
    Moderator
  • Hello Bro,

    In the above code how  you will generate the  accesstoken from GetAccessToken() method

    private string GetAccessToken()
    {
    	string accessToken;
    	
    	string CLIENT_ID = "<YOUR_CLIENT_ID>";
    	string CLIENT_SECRET = "<YOUR_CLIENT_SECRET>";
    	string REFRESH_TOKEN = "<YOUR_REFRESH_TOKEN>";
    	string REDIRECT_URL = "<YOUR_REDIRECT_URL>";
    
    	string param = "client_id=" + CLIENT_ID + "&redirect_uri=" + REDIRECT_URL + "&client_secret=" + CLIENT_SECRET + "&refresh_token=" + REFRESH_TOKEN + "&grant_type=refresh_token";
    
    	var client = new RestClient("https://login.microsoftonline.com/common/oauth2/v2.0/token");
    	var request = new RestRequest(Method.POST);
    	request.AddHeader("content-type", "application/x-www-form-urlencoded");
    	request.AddParameter("application/x-www-form-urlencoded", param, ParameterType.RequestBody);
    	var response = client.Execute(request);
    	try
    	{
    		var json = JObject.Parse(response.Content);
    
    		if (!string.IsNullOrEmpty(Convert.ToString(json["error_description"])))
    		{
    			HttpContext.Current.Response.Redirect("/OneDriveLoginPage.aspx?redirect=" + HttpContext.Current.Request.RawUrl);
    		}
    		accessToken = Convert.ToString(json["access_token"]);
    		string refreshToken = Convert.ToString(json["refresh_token"]);
    		
    		//keep your refresh token saved somewhere
    	}
    	catch (Exception ex)
    	{
    		throw ex;
    	}
    		
    	return accessToken;
    }


    - Mohamed Thaufeeq A

    Thursday, May 14, 2020 6:48 AM
  • Thanks for Response Bro:)

    Thursday, May 14, 2020 1:39 PM