Sample code for 30+ languages & platforms
C

OneDrive -- Upload Large Files with an Upload Session

See more OneDrive Examples

Demonstrates how to upload large files with an upload session. See OneDrive Upload Session for more general information.

Chilkat C Downloads

C
#include <C_CkJsonObject.h>
#include <C_CkHttp.h>
#include <C_CkHttpResponse.h>
#include <C_CkFileAccess.h>
#include <C_CkUrl.h>
#include <C_CkHttpRequest.h>
#include <C_CkStringBuilder.h>

void ChilkatSample(void)
    {
    BOOL success;
    HCkJsonObject json;
    HCkHttp http;
    const char *url;
    HCkHttpResponse resp;
    HCkJsonObject jsonSession;
    int fragSize;
    const char *localFilePath;
    HCkFileAccess fac;
    int fileSize;
    int numFragments;
    int i;
    HCkUrl uploadUrl;
    HCkHttpRequest req;
    HCkStringBuilder sbOffset;
    HCkStringBuilder sbNumBytes;
    HCkStringBuilder sbRange;
    int bytesRemaining;
    int chunkSize;
    int expectedStatusCode;
    int numReplaced;
    const char *domain;

    success = FALSE;

    //  This example requires the Chilkat API to have been previously unlocked.
    //  See Global Unlock Sample for sample code.

    //  This example uses the OAuth client credentials flow.
    //  See How to Create an Azure App Registration for OAuth 2.0 Client Credentials

    //  Use your client ID, client secret, and tenant ID in the following lines
    json = CkJsonObject_Create();
    CkJsonObject_UpdateString(json,"client_id","2871da2c-8176-4b7f-869b-2311aa82e743");
    CkJsonObject_UpdateString(json,"client_secret","2hu9Q~r5QuryUcEkNbg1btLtnfU1VUXzhSCG6brH");
    CkJsonObject_UpdateString(json,"scope","https://graph.microsoft.com/.default");
    CkJsonObject_UpdateString(json,"token_endpoint","https://login.microsoftonline.com/114d7ed6-71bf-4dbe-a866-748364121bf2/oauth2/v2.0/token");

    http = CkHttp_Create();
    CkHttp_putAuthToken(http,CkJsonObject_emit(json));

    //  ----------------------------------------------------------------------------
    //  Step 1: Create an upload session

    //  To begin a large file upload, your app must first request a new upload session. This creates a 
    //  temporary storage location where the bytes of the file will be saved until the complete file is uploaded. 
    //  Once the last byte of the file has been uploaded the upload session is completed and the final file is shown 
    //  in the destination folder.

    //  Send the following POST to create an upload session:
    //  If not using "me", then the path should be /v1.0/users/{id | userPrincipalName}/...
    //  POST /v1.0/users/{user_id}/drive/root:/{path_to_item}:/createUploadSession

    CkHttp_SetUrlVar(http,"path_to_item","/somefolder/big.zip");
    CkHttp_SetUrlVar(http,"user_id","4fe732c3-322e-4a6b-b729-2fd1eb5c6104");
    url = "https://graph.microsoft.com/v1.0/users/{$user_id}/drive/root:/{$path_to_item}:/createUploadSession";
    resp = CkHttpResponse_Create();
    success = CkHttp_HttpStr(http,"POST",url,"{}","utf-8","application/json",resp);
    if (success == FALSE) {
        printf("%s\n",CkHttp_lastErrorText(http));
        CkJsonObject_Dispose(json);
        CkHttp_Dispose(http);
        CkHttpResponse_Dispose(resp);
        return;
    }

    //  If successful, a 200 status code is returned, with the session details (in JSON format).
    jsonSession = CkJsonObject_Create();
    CkJsonObject_putEmitCompact(jsonSession,FALSE);
    CkJsonObject_Load(jsonSession,CkHttpResponse_bodyStr(resp));

    if (CkHttpResponse_getStatusCode(resp) != 200) {

        printf("%s\n",CkJsonObject_emit(jsonSession));
        printf("Response status = %d\n",CkHttpResponse_getStatusCode(resp));
        CkJsonObject_Dispose(json);
        CkHttp_Dispose(http);
        CkHttpResponse_Dispose(resp);
        CkJsonObject_Dispose(jsonSession);
        return;
    }

    printf("%s\n",CkJsonObject_emit(jsonSession));

    //  A sample response:

    //  	{
    //  	  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#microsoft.graph.uploadSession",
    //  	  "uploadUrl": "https://api.onedrive.com/rup/3a33fceb9b74cc15/eyJSZXNvdXJjZUlEI ... 65yDYUiS3JTDnnhqCHxw",
    //  	  "expirationDateTime": "2017-06-11T12:40:23.239Z",
    //  	  "nextExpectedRanges": [
    //  	    "0-"
    //  	  ]
    //  	}
    //  

    //  ----------------------------------------------------------------------------
    //  Step 2: Upload Data in Segments (a.k.a. Fragments or Chunks)

    //  Microsoft states this requirement: Use a fragment size that is a multiple of 320 KiB (320 * 1024 bytes). 
    //  Failing to use a fragment size that is a multiple of 320 KiB can result in large file transfers failing after the 
    //  last fragment is uploaded.  (Note: This is a detail imposed by Microsoft's OneDrive server-side implementation.)

    fragSize = 320 * 1024;
    localFilePath = "qa_data/zips/big.zip";

    //  Upload the file big.zip in 320KiB segments.
    //  Note: The FileSize method returns a signed 32-bit integer.  If the file is potentially larger than 2GB, call FileSizeStr instead to return
    //  the size of the file as a string, then convert to an integer value.
    fac = CkFileAccess_Create();
    fileSize = CkFileAccess_FileSize(fac,localFilePath);

    //  Open the file to get the number of fragments.
    success = CkFileAccess_OpenForRead(fac,localFilePath);
    if (success == FALSE) {
        printf("%s\n",CkFileAccess_lastErrorText(fac));
        CkJsonObject_Dispose(json);
        CkHttp_Dispose(http);
        CkHttpResponse_Dispose(resp);
        CkJsonObject_Dispose(jsonSession);
        CkFileAccess_Dispose(fac);
        return;
    }

    numFragments = CkFileAccess_GetNumBlocks(fac,fragSize);
    CkFileAccess_FileClose(fac);

    i = 0;

    printf("fileSize = %d\n",fileSize);
    printf("numFragments = %d\n",numFragments);

    uploadUrl = CkUrl_Create();
    CkUrl_ParseUrl(uploadUrl,CkJsonObject_stringOf(jsonSession,"uploadUrl"));

    CkJsonObject_putEmitCompact(json,FALSE);

    req = CkHttpRequest_Create();
    CkHttpRequest_putHttpVerb(req,"PUT");
    CkHttpRequest_putPath(req,CkUrl_pathWithQueryParams(uploadUrl));
    CkHttpRequest_putContentType(req,"application/octet-stream");

    sbOffset = CkStringBuilder_Create();
    sbNumBytes = CkStringBuilder_Create();
    sbRange = CkStringBuilder_Create();

    //  IMPORTANT: The uploadUrl is a temporary URL to be used to upload the fragment.
    //  It requires no authentication (because the URL itself could only have been obtained from an authenticated
    //  request to start the upload session).  Therefore, do not allow the upload URL to be publicly seen,
    //  otherwise anybody could upload to your OneDrive.
    CkHttp_putAuthToken(http,"");

    bytesRemaining = fileSize;
    while (i < numFragments) {

        //  The success response code for intermediate chunks is 202,
        //  whereas the final chunk will have a 201 success response where
        //  the response body is the JSON DriveItem.
        chunkSize = fragSize;
        expectedStatusCode = 202;
        if (bytesRemaining < chunkSize) {
            chunkSize = bytesRemaining;
            expectedStatusCode = 201;
        }

        printf("  this chunkSize = %d\n",chunkSize);

        //  Indicate the fragment in the local file to be streamed in the upload.
        CkStringBuilder_Clear(sbOffset);
        CkStringBuilder_AppendInt(sbOffset,i * fragSize);
        CkStringBuilder_Clear(sbNumBytes);
        CkStringBuilder_AppendInt(sbNumBytes,chunkSize);
        CkHttpRequest_StreamChunkFromFile(req,localFilePath,CkStringBuilder_getAsString(sbOffset),CkStringBuilder_getAsString(sbNumBytes));

        //  The Content-Range header field must be set for this fragment.  For example:
        //  Content-Range: bytes 0-25/128
        CkStringBuilder_SetString(sbRange,"bytes start-end/fileSize");
        numReplaced = CkStringBuilder_ReplaceI(sbRange,"start",i * fragSize);
        numReplaced = CkStringBuilder_ReplaceI(sbRange,"end",i * fragSize + chunkSize - 1);
        numReplaced = CkStringBuilder_ReplaceI(sbRange,"fileSize",fileSize);
        CkHttpRequest_AddHeader(req,"Content-Range",CkStringBuilder_getAsString(sbRange));
        printf("  this content-range: %s\n",CkStringBuilder_getAsString(sbRange));

        domain = CkUrl_host(uploadUrl);
        success = CkHttp_HttpSReq(http,domain,443,TRUE,req,resp);
        if (success == FALSE) {
            printf("%s\n",CkHttp_lastErrorText(http));
            CkJsonObject_Dispose(json);
            CkHttp_Dispose(http);
            CkHttpResponse_Dispose(resp);
            CkJsonObject_Dispose(jsonSession);
            CkFileAccess_Dispose(fac);
            CkUrl_Dispose(uploadUrl);
            CkHttpRequest_Dispose(req);
            CkStringBuilder_Dispose(sbOffset);
            CkStringBuilder_Dispose(sbNumBytes);
            CkStringBuilder_Dispose(sbRange);
            return;
        }

        CkJsonObject_Load(json,CkHttpResponse_bodyStr(resp));
        //  A 202 response status code indicates success.
        if (CkHttpResponse_getStatusCode(resp) != expectedStatusCode) {

            printf("%s\n",CkJsonObject_emit(json));
            printf("Response status = %d\n",CkHttpResponse_getStatusCode(resp));
            CkJsonObject_Dispose(json);
            CkHttp_Dispose(http);
            CkHttpResponse_Dispose(resp);
            CkJsonObject_Dispose(jsonSession);
            CkFileAccess_Dispose(fac);
            CkUrl_Dispose(uploadUrl);
            CkHttpRequest_Dispose(req);
            CkStringBuilder_Dispose(sbOffset);
            CkStringBuilder_Dispose(sbNumBytes);
            CkStringBuilder_Dispose(sbRange);
            return;
        }

        printf("%s\n",CkJsonObject_emit(json));
        printf("---- Chunk %d uploaded ----\n",i);

        bytesRemaining = bytesRemaining - chunkSize;
        i = i + 1;
    }

    printf("data uploaded.\n");

    //  ----------------------------------------------------------------------------
    //  Sample output for the above session:

    //  {
    //    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#microsoft.graph.uploadSession",
    //    "uploadUrl": "https://api.onedrive.com/rup/3a33fceb9b74cc15/eyJSZXNvd ... QoKK2iuh1A",
    //    "expirationDateTime": "2017-06-11T14:04:45.438Z",
    //    "nextExpectedRanges": [
    //      "0-"
    //    ]
    //  }
    //  
    //  fileSize = 1366807
    //  numFragments = 5
    //    this chunkSize = 327680
    //    this content-range: bytes 0-327679/1366807
    //  {
    //    "expirationDateTime": "2017-06-11T14:04:45.438Z",
    //    "nextExpectedRanges": [
    //      "327680-1366806"
    //    ]
    //  }
    //  
    //  ---- Chunk 0 uploaded ----
    //    this chunkSize = 327680
    //    this content-range: bytes 327680-655359/1366807
    //  {
    //    "expirationDateTime": "2017-06-11T14:04:45.438Z",
    //    "nextExpectedRanges": [
    //      "655360-1366806"
    //    ]
    //  }
    //  
    //  ---- Chunk 1 uploaded ----
    //    this chunkSize = 327680
    //    this content-range: bytes 655360-983039/1366807
    //  {
    //    "expirationDateTime": "2017-06-11T14:04:45.438Z",
    //    "nextExpectedRanges": [
    //      "983040-1366806"
    //    ]
    //  }
    //  
    //  ---- Chunk 2 uploaded ----
    //    this chunkSize = 327680
    //    this content-range: bytes 983040-1310719/1366807
    //  {
    //    "expirationDateTime": "2017-06-11T14:04:45.438Z",
    //    "nextExpectedRanges": [
    //      "1310720-1366806"
    //    ]
    //  }
    //  
    //  ---- Chunk 3 uploaded ----
    //    this chunkSize = 56087
    //    this content-range: bytes 1310720-1366806/1366807
    //  {
    //    "createdBy": {
    //      "application": {
    //        "displayName": "Chilkat",
    //        "id": "441c9990"
    //      },
    //      "user": {
    //        "id": "3a33fceb9b74cc15"
    //      }
    //    },
    //    "createdDateTime": "2017-06-04T14:04:47.247Z",
    //    "cTag": "aYzozQTMzRkNFQjlCNzRDQzE1ITQ4NjguMjU3",
    //    "eTag": "aM0EzM0ZDRUI5Qjc0Q0MxNSE0ODY4LjA",
    //    "id": "3A33FCEB9B74CC15!4868",
    //    "lastModifiedBy": {
    //      "application": {
    //        "displayName": "Chilkat",
    //        "id": "441c9990"
    //      },
    //      "user": {
    //        "id": "3a33fceb9b74cc15"
    //      }
    //    },
    //    "lastModifiedDateTime": "2017-06-04T14:04:47.247Z",
    //    "name": "big.zip",
    //    "parentReference": {
    //      "driveId": "3a33fceb9b74cc15",
    //      "id": "3A33FCEB9B74CC15!4862",
    //      "name": "someFolder",
    //      "path": "/drive/root:/someFolder"
    //    },
    //    "size": 1366807,
    //    "webUrl": "https://1drv.ms/u/s!ABXMdJvr_DM6pgQ",
    //    "file": {
    //      "hashes": {
    //        "sha1Hash": "252059AA13004220DB912B97D4D3FF9599CCD8D9"
    //      },
    //      "mimeType": "application/zip"
    //    },
    //    "fileSystemInfo": {
    //      "createdDateTime": "2017-06-04T14:04:47.246Z",
    //      "lastModifiedDateTime": "2017-06-04T14:04:47.246Z"
    //    },
    //    "tags": [
    //    ],
    //    "lenses": [
    //    ]
    //  }
    //  
    //  Response status = 201


    CkJsonObject_Dispose(json);
    CkHttp_Dispose(http);
    CkHttpResponse_Dispose(resp);
    CkJsonObject_Dispose(jsonSession);
    CkFileAccess_Dispose(fac);
    CkUrl_Dispose(uploadUrl);
    CkHttpRequest_Dispose(req);
    CkStringBuilder_Dispose(sbOffset);
    CkStringBuilder_Dispose(sbNumBytes);
    CkStringBuilder_Dispose(sbRange);

    }