Resumable Upload of Large Files to OneDrive via SPFx & .Net App

In Office 365, There is always a project requirement to upload files either to SharePoint Online document libraries or to Microsoft OneDrive. In this blog post, We will look into uploading large files to Microsft OneDrive using Graph API via SPFx and .Net App .

Uploading Files to OneDrive via SPFx

In the below section, we will look into uploading small and large files to OneDrive using Microsoft Graph API via the SharePoint Framework web part.

Uploading Small files to OneDrive

If your file is less than 4MB in size, then The recommendation is to use the simple upload API implementation to provide the contents of a new file or update the contents of an existing file in a single API call.

Upload or replace the contents of a DriveItem

You can use the below code snippet to upload small files [less than 4MB in size]

/**
* Upload files [less than 4MB in size] to OneDrive
* @param file
* @returns Uploaded file
*/
public static async UploadSmallFile(file: File): Promise<any> {
try {
const fileContent = await FileHelper.readFileContent(file);
let uploadedFile: any = await this._graphClient.api(`/me/drive/root:/${file.name}:/content`).put(fileContent);

return uploadedFile;

}
catch (error) {
console.log("Error in UploadSmallFile:", error);
return null;
}
}

Uploading Large Files to OneDrive

Typically, when dealing with time-consuming operations, we use Azure Functions for these kinds of tasks. This blog post will show how we can upload large files to OneDrive using Microsoft Graph API via the SPFx web part.

To upload a file using an upload session, there are two steps:

  1. Create an upload session
  2. Upload bytes to the upload session

Uploading Large Files to OneDrive

Permissions

The following permissions are required to call this API from the SPFx web part.

Permission type Permissions (from least to most privileged)
Delegated (work or school account) Files.ReadWrite, Files.ReadWrite.All, Sites.ReadWrite.All

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.

Alternatively, you can defer final creation of the file in the destination until you explicitly make a request to complete the upload, by setting the deferCommit property in the request arguments.

Code Snippet to Create Upload session

//payload for OneDrive
const payload = {
"@microsoft.graph.conflictBehavior": "rename",
"description": "description",
"fileSize": file.size,
"name": `${file.name}`
};

//Create the upload session
const requestURL = `/me/drive/root:/Uploads/${file.name}:/createUploadSession`;
const uploadSessionRes = await props.graphClient.api(requestURL).post(payload);
const uploadEndpoint: string = uploadSessionRes.uploadUrl;

Upload bytes to the upload session

To upload the file, or a portion of the file, your app makes a PUT request to the uploadUrl value received in the createUploadSession response.
You can upload the entire file, or split the file into multiple byte ranges, as long as the maximum bytes in any given request is less than 60 MiB.

The fragments of the file must be uploaded sequentially in order.
Uploading fragments out of order will result in an error.

Note

If your app splits a file into multiple byte ranges, the size of each byte range must be a multiple of 320 KiB (327,680 bytes). Using a fragment size that does not divide evenly by 320 KiB will result in errors committing some files..

Code Snippet to Upload File in Chunks

//Get file content      
const fileBuffer = await file.arrayBuffer();

// Maximum file chunk size
const FILE_CHUNK_SIZE = 320 * 1024 // 0.32 MB;

//Total number of chunks for given file
const NUM_CHUNKS = Math.floor(fileBuffer.byteLength / FILE_CHUNK_SIZE) + 1;

//Counter for building progress bar
let counter = 1;

//Initial value of upload index
let uploadIndex: number = 0;

while (true) {

//Get the current progress bar status
const progressValue = parseFloat((counter / NUM_CHUNKS).toFixed(2));
setPercentComplete(progressValue);

//Calculate the end index
let endIndex = uploadIndex + FILE_CHUNK_SIZE - 1;

//Gets the slice
let slice: ArrayBuffer;
if (endIndex >= fileBuffer.byteLength) {
endIndex = fileBuffer.byteLength - 1;
slice = fileBuffer.slice(uploadIndex);
} else {
slice = fileBuffer.slice(uploadIndex, endIndex + 1);
}

//Upload file
const headers = {
'Content-Length': `${slice.byteLength}`,
'Content-Range': `bytes ${uploadIndex}-${endIndex}/${fileBuffer.byteLength}`
};

LogHelper.info("", "", "Uploading chunk:" + `${uploadIndex}-${endIndex}`);

const response = await props.graphClient.api(uploadEndpoint).headers(headers).put(slice);
if (!response) {
break;
}
if (response.nextExpectedRanges) {
//Get the next expected range of the slice
uploadIndex = parseFloat(response.nextExpectedRanges[0]);
counter++;
} else {
//if there is no next range then break the loop
//Gets the upoaded file response
result = response
break;
}

}

Note

You can find the complete source code from GitHub.

Demo

DEMO: Uploading Large Files to OneDrive

Uploading Large Files to OneDrive via .Net App

You can use the following code snippet to upload large files via C# .Net app using Microsoft Graph .NET Client Library

public async Task UploadLargeFile(GraphServiceClient client)
{


var fileName = "largefile.zip";
var filePath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), fileName);

// load resource as a stream
using (Stream stream = new FileStream(filePath, FileMode.Open))
{
var uploadSession = await client.Me.Drive.Root
.ItemWithPath(fileName)
.CreateUploadSession()
.Request()
.PostAsync();


// create upload task
var maxChunkSize = 320 * 1024;
var largeUploadTask = new LargeFileUploadTask<DriveItem>(uploadSession, stream, maxChunkSize);

// create progress implementation
IProgress<long> uploadProgress = new Progress<long>(uploadBytes =>
{
Console.WriteLine($"Uploaded {uploadBytes} bytes of {stream.Length} bytes");
});

// upload file
UploadResult<DriveItem> uploadResult = await largeUploadTask.UploadAsync(uploadProgress);
if (uploadResult.UploadSucceeded)
{
Console.WriteLine("File uploaded to user's OneDrive root folder.");
}
}

}
Author: Ejaz Hussain
Link: https://office365clinic.com/2022/08/05/resumable-upload-of-large-files-to-onedrive/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.