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);

    }