A number of entities in Microsoft Graph support resumable file uploads to make it easier to upload large files. Instead of trying to upload the entire file in a single request, the file is sliced into smaller pieces and a request is used to upload a single slice. In order to simplify this process, the Microsoft Graph SDKs implement a large file upload task that manages the uploading of the slices.
using var fileStream = System.IO.File.OpenRead(filePath);
// Use properties to specify the conflict behavior
// in this case, replace
var uploadSessionRequestBody = new CreateUploadSessionPostRequestBody
{
Item = new DriveItemUploadableProperties
{
AdditionalData = new Dictionary<string, object>
{
{ "@microsoft.graph.conflictBehavior", "replace" }
}
}
};
// Create the upload session
// itemPath does not need to be a path to an existing item
var uploadSession = await graphClient.Drive.Root
.ItemWithPath(itemPath)
.CreateUploadSession
.PostAsync(uploadSessionRequestBody);
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask = new LargeFileUploadTask<DriveItem>(uploadSession, fileStream, maxSliceSize, graphClient.RequestAdapter);
var totalLength = fileStream.Length;
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(prog => {
Console.WriteLine($"Uploaded {prog} bytes of {totalLength} bytes");
});
try
{
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
Console.WriteLine(uploadResult.UploadSucceeded ?
$"Upload complete, item ID: {uploadResult.ItemResponse.Id}" :
"Upload failed");
}
catch (ServiceException ex)
{
Console.WriteLine($"Error uploading: {ex.ToString()}");
}
const options: OneDriveLargeFileUploadOptions = {
// Relative path from root to destination folder
path: itemPath,
// file is a File object, typically from an <input type="file"/>
fileName: file.name,
rangeSize: 1024 * 1024,
uploadEventHandlers: {
// Called as each "slice" of the file is uploaded
progress: (range, e) => {
console.log(`Uploaded ${range?.minValue} to ${range?.maxValue}`);
}
}
};
try {
// Create FileUpload object
const fileObject = new FileUpload(file, file.name, file.size);
// Create a OneDrive upload task
const uploadTask = await OneDriveLargeFileUploadTask
.createTaskWithFileObject(client, fileObject, options);
// Do the upload
const uploadResult: UploadResult = await uploadTask.upload();
// The response body will be of the corresponding type of the
// item being uploaded. For OneDrive, this is a DriveItem
const driveItem = uploadResult.responseBody as DriveItem;
console.log(`Uploaded file with ID: ${driveItem.id}`);
return `Uploaded file with ID: ${driveItem.id}`;
} catch (err) {
console.log(`Error uploading file: ${JSON.stringify(err)}`);
return `Error uploading file: ${JSON.stringify(err)}`;
}
As alternatives to using a File object to create a FileUpload, you can use a ReadStream object to create a StreamUpload.
// Get an input stream for the file
File file = new File(localFilePath);
InputStream fileStream = new FileInputStream(file);
long streamSize = file.length();
// Create a callback used by the upload provider
IProgressCallback callback = new IProgressCallback() {
@Override
// Called after each slice of the file is uploaded
public void progress(final long current, final long max) {
System.out.println(
String.format("Uploaded %d bytes of %d total bytes", current, max)
);
}
};
DriveItemCreateUploadSessionParameterSet uploadParams =
DriveItemCreateUploadSessionParameterSet.newBuilder()
.withItem(new DriveItemUploadableProperties()).build();
// Create an upload session
UploadSession uploadSession = graphClient
.me()
.drive()
.root()
// itemPath like "/Folder/file.txt"
// does not need to be a path to an existing item
.itemWithPath(itemPath)
.createUploadSession(uploadParams)
.buildRequest()
.post();
LargeFileUploadTask<DriveItem> largeFileUploadTask =
new LargeFileUploadTask<DriveItem>
(uploadSession, graphClient, fileStream, streamSize, DriveItem.class);
// Do the upload
largeFileUploadTask.upload(0, null, callback);
Resuming a file upload
The Microsoft Graph SDKs support resuming in-progress uploads. If your application encounters a connection interruption or a 5.x.x HTTP status during upload, you can resume the upload.
// Create message
var draftMessage = new Message
{
Subject = "Large attachment"
};
var savedDraft = await graphClient.Me
.Messages
.Request()
.AddAsync(draftMessage);
using var fileStream = System.IO.File.OpenRead(filePath);
var largeAttachment = new AttachmentItem
{
AttachmentType = AttachmentType.File,
Name = "largefile.gif",
Size = fileStream.Length
};
var uploadSession = await graphClient.Me
.Messages[savedDraft.Id]
.Attachments
.CreateUploadSession(largeAttachment)
.Request()
.PostAsync();
// Max slice size must be a multiple of 320 KiB
int maxSliceSize = 320 * 1024;
var fileUploadTask =
new LargeFileUploadTask<FileAttachment>(uploadSession, fileStream, maxSliceSize);
var totalLength = fileStream.Length;
// Create a callback that is invoked after each slice is uploaded
IProgress<long> progress = new Progress<long>(prog => {
Console.WriteLine($"Uploaded {prog} bytes of {totalLength} bytes");
});
try
{
// Upload the file
var uploadResult = await fileUploadTask.UploadAsync(progress);
Console.WriteLine(uploadResult.UploadSucceeded ? "Upload complete" : "Upload failed");
}
catch (ServiceException ex)
{
Console.WriteLine($"Error uploading: {ex.ToString()}");
}
// Create an attachment item payload
// file is a File object
const payload = {
AttachmentItem: {
attachmentType: 'file',
name: file.name,
size: file.size
}
};
const options: LargeFileUploadTaskOptions = {
rangeSize: 1024 * 1024,
// Called as each "slice" of the file is uploaded
uploadEventHandlers: {
progress: (range, e) => {
console.log(`Uploaded ${range?.minValue} to ${range?.maxValue}`);
}
}
};
try {
// Create a draft message
const draftMsg: Message = await client.api('/me/messages')
.post({
subject: 'Large file attachment'
});
// Create upload session using draft message's ID
const uploadSession = await LargeFileUploadTask
.createUploadSession(client,
`/me/messages/${draftMsg.id}/attachments/createUploadSession`,
payload);
// Create file upload
// Note you can use StreamUpload or custom implementation of FileObject instead
const fileObject = new FileUpload(file, file.name, file.size);
// Create upload task
const uploadTask = new LargeFileUploadTask(client, fileObject, uploadSession, options);
// Upload the file
const uploadResult: UploadResult = await uploadTask.upload();
return 'Attachment uploaded';
} catch (err) {
console.log(`Error uploading file: ${JSON.stringify(err)}`);
return `Error uploading file: ${JSON.stringify(err)}`;
}
final String[] scopes = { "Mail.ReadWrite" };
ensureGraphClient(scopes);
final String localFilePath = "largefile.gif";
final Message draftMessage = new Message();
draftMessage.subject = "Large attachment";
final Message savedDraft = graphClient
.me()
.messages()
.buildRequest()
.post(draftMessage);
File file = new File(localFilePath);
// Get an input stream for the file
InputStream fileStream = new FileInputStream(file);
final AttachmentItem largeAttachment = new AttachmentItem();
largeAttachment.attachmentType = AttachmentType.FILE;
largeAttachment.name = "largefile.gif";
largeAttachment.size = file.length();
final AttachmentCreateUploadSessionParameterSet upParams =
AttachmentCreateUploadSessionParameterSet.newBuilder()
.withAttachmentItem(largeAttachment)
.build();
final UploadSession uploadSession = graphClient
.me()
.messages(savedDraft.id)
.attachments()
.createUploadSession(upParams)
.buildRequest()
.post();
// Create a callback used by the upload provider
IProgressCallback callback = new IProgressCallback() {
@Override
// Called after each slice of the file is uploaded
public void progress(final long current, final long max) {
System.out.println(
String.format("Uploaded %d bytes of %d total bytes", current, max)
);
}
};
LargeFileUploadTask<FileAttachment> uploadTask =
new LargeFileUploadTask<FileAttachment>
(uploadSession, graphClient, fileStream, file.length(), FileAttachment.class);
// Do the upload
uploadTask.upload(0, null, callback);