Skip to the content.

File Upload Task

Objectives

Uploading a large file can be done by creating an upload session which allows the upload of slices of the file in sequential API requests. This therefore enables the possibility mechanisms such as resuming the upload or cancelling the upload. This task aims to provide a fluent and easy to use mechanism for the consumer to control, monitor and cancel the handling of large uploads.

Requirements

Note: Outlook and Print API does not allow to update an attachment after it has been uploaded.

Refer to the following documentation for more information:

Large File Upload Session

Json Schema of the upload session object:

{
  "properties": {
    "url": { "type": "string"},
    "expiryDate": { "type": "date"},
    "isCancelled": { "type": "boolean"},
  }
}

Large File Upload Result Prototype

Json Schema with a generic type for the object:

{
  "type": "object",
  "properties": {
    "location": { "type": "string"},
    "responseBody": { "type":  "generic"},
  }
}

Warning: the location header casing can be different between HTTP/1.1 Location and HTTP/2. Implementation should account for this.

Large Upload Sequence

  1. The consumer creates a large upload session using the SDK.
  2. The consumer opens a stream to the file that needs to be uploaded (from storage, network, memory…).
  3. The consumer creates a large upload task (this object model) passing the upload session, upload parameters, and the stream.
  4. The consumer calls the upload method which:
    1. Reads the next bytes according to parameters
    2. Performs the upload request.
    3. Reads the response to determine whether a next range of bytes is expected, or the upload is completed, or whether the upload has failed.
    4. If next bytes are expected, the task repeats previous 3 steps.
  5. The upload status is returned to the consumer or an exception is thrown if the upload failed.

Large File Progress Handler Prototype

{
  "properties":{
    "progress":{"type": "callback"},
    "extraCallbackParam": { "type":  "generic"}
  }
}

Example of a response calling for the upload of the next range

{
  "@odata.context":"https://outlook.office.com/api/v2.0/$metadata#Users('<redacted>')/Messages('<redacted>')/AttachmentSessions/$entity",
  "expirationDateTime":"2019-09-25T01:09:30.7671707Z",
  "nextExpectedRanges":["2097152"]
}

Warning: In case the large upload task is targeting Outlook APIs, it is possible the casing of the json properties is different (i.e. ExpirationDateTime instead of expirationDateTime). Implementation should account for this.

Performance Considerations

Telemetry considerations

Example usage

CSharp

Create a handler for the progress of the upload

// Setup the progress monitoring
IProgress<long> progress = new Progress<long>(progress =>
{
    Console.WriteLine($"Uploaded {progress} bytes of {stream.Length} bytes");
});

Create an upload session using the request builders

// create an upload session
var uploadSession = await graphClient.Drive.Items["01KGPRHTV6Y2GOVW7725BZO354PWSELRRZ"].ItemWithPath("_hamilton.png").CreateUploadSession().Request().PostAsync();

Use the upload session and callback handler to run/resume the upload

// create the Large File Upload task with relevant options
var maxSliceSize = 320 * 1024; // 320 KB - Change this to your slice size. 5MB is the default.
// var provider = new ChunkedUploadProvider(uploadSession, graphClient, stream, maxChunkSize); //Old way that is wrapped
var largeFileUploadTask = new LargeFileUploadTask(uploadSession, graphClient, stream, maxSliceSize);

// upload away with relevant callback
LargeFileUploadResult<DriveItem> uploadResult = await largeFileUploadTask.UploadAsync( progress );

Java

// Define a callback for the upload progress
final IProgressCallback callback = new IProgressCallback () {
    @Override
    public void progress(final long current, final long max) {
        //Check progress
    }
};

Create an upload session using the request builders

// create an upload session
final IUploadSession uploadSession = testBase
        .graphClient
        .me()
        .drive()
        .items(itemId)
        .itemWithPath("_hamilton.jpg")
        .createUploadSession(new DriveItemUploadableProperties())
        .buildRequest()
        .post();

Use the upload session and callback handler to run the upload

// create the upload provider
final LargeFileUploadTask<DriveItem> largeFileUploadTask = new LargeFileUploadTask<DriveItem>(
        uploadSession,
        graphClient,
        uploadFile,  //the file to upload
        fileSize,    //size of the slices
        DriveItem.class);

// upload with the provided callback mechanism
final LargeFileUploadResult<DriveItem> result = largeFileUploadTask.upload(callback); //or uploadAsync to get a future

TypeScript

Create an upload session using the request builders


// create the upload session
const uploadSession = LargeFileUploadTask.createUploadSession(client, "REQUEST_URL", payload);

// specify the options
let options = {
    path: "/Documents",
    fileName: file.name,
    rangeSize: 1024 * 1024,
};

// Create an upload session
const uploadTask = new LargeFileUploadTask(client, fileObj, uploadSession, optionsWithProgress);

//Use the upload task to run the upload
const uploadResult:UploadResult = await uploadTask.upload();