Skip to content

Support deferCommit in LargeFileUploadTask for upload sessions #984

@ibaumann

Description

@ibaumann

I want to defer the final creation of the file being uploaded via an upload session in the destination (in this case, SharePoint Embedded) until I explicitly make a request to complete the upload. For this, I'm setting the deferCommit property in the request arguments for the creation of the upload session, as indicated in Create an upload session - driveItem: createUploadSession | MS Learn.

When using LargeFileUploadTask to use the created upload session, as indicated in Upload large files using the Microsoft Graph SDKs | MS Learn, the upload task does not stop when all the bytes have been uploaded and the server returns that there are no nextExpectedRanges in the 202 response. It starts a loop to retrieve the status of the upload session and the expected ranges (which in return are empty) until all retry attempts have been exhausted. Then, it throws an exception saying Upload failed too many times..

From source: LargeFileUploadTask.cs

public async Task<UploadResult<T>> UploadAsync(IProgress<long> progress = null, int maxTries = 3, CancellationToken cancellationToken = default)
{
    var uploadTries = 0;
    var trackedExceptions = new List<Exception>();

    while (uploadTries < maxTries)
    {
        var sliceRequests = this.GetUploadSliceRequests();

        foreach (var request in sliceRequests)
        {
            var uploadResult = await this.UploadSliceAsync(request, trackedExceptions, cancellationToken).ConfigureAwait(false);

            progress?.Report(request.RangeEnd);//report the progress of upload (how many bytes have been uploaded so far)

            if (uploadResult.UploadSucceeded) // **<--- from observation/testing: this is always false**
            {
                return uploadResult;
            }

            ThrowIfUploadCancelled(trackedExceptions, cancellationToken);
        }

        await this.UpdateSessionStatusAsync(cancellationToken).ConfigureAwait(false);
        uploadTries += 1;
        if (uploadTries < maxTries)
        {
            // Exponential back off in case of failures.
            await Task.Delay(2000 * uploadTries * uploadTries, cancellationToken).ConfigureAwait(false);
        }

        ThrowIfUploadCancelled(trackedExceptions, cancellationToken);
    }

    throw new TaskCanceledException("Upload failed too many times. See InnerException for list of exceptions that occured.", new AggregateException(trackedExceptions.ToArray()));
}

public async Task<IUploadSession> UpdateSessionStatusAsync(CancellationToken cancellationToken = default)
{
    var requestBuilder = new UploadSessionRequestBuilder(this.Session, this._requestAdapter);
    var newSession = await requestBuilder.GetAsync(cancellationToken).ConfigureAwait(false);

    var newRangesRemaining = this.GetRangesRemaining(newSession);  // **<--- from observation/testing: this return empty**

    this._rangesRemaining = newRangesRemaining;
    newSession.UploadUrl = this.Session.UploadUrl; // Sometimes the UploadUrl is not returned
    this.Session = newSession;
    return newSession;
}

Proposed solution

I would like LargeFileUploadTask to handle defer commits. Alternatives:

  1. Allow the caller to indicate that the upload is deferred during construction (i.e.: a parameter).
  2. The task itself can interpret the 202 response with no next expected ranges as having the commit deferred.

Once the task is done uploading, there could be a CommitSessionAsync method (in the same line as the DeleteSessionAsync method) to perform the commit request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions