Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 70 additions & 25 deletions docs/tasks/LargeFileUploadTask.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Large File Upload Task - Uploading large files to OneDrive.
# Large File Upload Task - Uploading large files to OneDrive

This task simplifies the implementation of onedrive's [resumable upload](https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/driveitem_createuploadsession).
This task simplifies the implementation of OneDrive's [resumable upload](https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/api/driveitem_createuploadsession).

## Creating the client instance

Expand All @@ -20,47 +20,60 @@ Get files from the input element and start uploading.
async function fileUpload(elem) {
let file = elem.files[0];
try {
let response = await largeFilUpload(client, file, file.name);
let response = await largeFileUpload(client, file, file.name);
console.log(response);
console.log("File Uploaded Successfully.!!");
} catch (error) {
console.error(error);
}
}

async function largeFileUpload(client, file) {
try {
let options = {
path: "/Documents",
fileName: file.name,
rangeSize: 1024 * 1024,
};
const uploadTask = await MicrosoftGraph.OneDriveLargeFileUploadTask.create(client, file, options);
const response = await uploadTask.upload();
return response;
} catch (err) {
throw err;
}
}
```

## Uploading from NodeJS

```typescript
function uploadFile() {
fs.readFile(<PATH_OF_THE_FILE>, {}, function (err, file) {
if(err) {
throw err;
}
let fileName = <NAME_OF_THE_FILE>;
largeFileUpload(client, file, fileName)
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error(error);
});
});
fs.readFile("<PATH_OF_THE_FILE>", {}, function(err, file) {
if (err) {
throw err;
}
let fileName = "<NAME_OF_THE_FILE_WITH_EXTN>";
oneDriveLargeFileUpload(client, file, fileName)
.then((response) => {
console.log(response);
console.log("File Uploaded Successfully.!!");
})
.catch((error) => {
throw err;
});
});
}
```

## Creating session and start uploading

```typescript
async function uploadFile(client, file) {
async function oneDriveLargeFileUpload(client, file, fileName) {
try {
let options = {
path: "/Documents",
fileName: file.name,
fileName,
rangeSize: 1024 * 1024,
};
const uploadTask = await MicrosoftGraph.OneDriveLargeFileUploadTask.create(client, file, options);
const uploadTask = await OneDriveLargeFileUploadTask.create(client, file, options);
const response = await uploadTask.upload();
console.log(response);
console.log("File Uploaded Successfully.!!");
return response;
} catch (err) {
console.log(err);
}
Expand All @@ -84,3 +97,35 @@ let range = uploadTask.getNextRange();
let slicedFile = uploadTask.sliceFile(range);
uploadTask.uploadSlice(slicedFile, range, uploadTask.file.size);
```

## Uploading with custom options

_You can pass in the customized options using LargeFileUploadTask_

```typescript
async function largeFileUpload(client, file) {
const filename = file.name;
const driveId = "<YOUR_DRIVE_ID>";
const path = "LOCATION_TO_STORE_FILE";
try {
const requestUrl = `/drives/${driveId}/root:${path}/${fileName}:/createUploadSession`;
const payload = {
item: {
"@microsoft.graph.conflictBehavior": "fail",
name: fileName,
},
};
const fileObject = {
size: file.size,
content: file,
name: fileName,
};
const uploadSession = await LargeFileUploadTask.createUploadSession(client, requestUrl, payload);
const uploadTask = await new LargeFileUploadTask(client, fileObject, uploadSession);
const response = await uploadTask.upload();
return response;
} catch (err) {
throw err;
}
}
```
22 changes: 11 additions & 11 deletions spec/tasks/LargeFileUploadTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { LargeFileUploadTask } from "../../src/tasks/LargeFileUploadTask";
import { getClient } from "../test-helper";

describe("LargeFileUploadTask.ts", () => {
/* tslint:disable: no-string-literal */
describe("Parsing Range", () => {
const name = "sample_image.jpg";
const arrayBuffer = new ArrayBuffer(80000);
Expand All @@ -27,34 +28,33 @@ describe("LargeFileUploadTask.ts", () => {
const options = {};
const uploadTask = new LargeFileUploadTask(getClient(), fileObj, uploadSession, options);
it("Should return default range for given undefined range", (done) => {
const range = uploadTask.parseRange([]);
const range = uploadTask["parseRange"]([]);
assert.equal(range.minValue, -1);
assert.equal(range.maxValue, -1);
done();
});

it("Should return default range for given empty range", (done) => {
const range = uploadTask.parseRange([""]);
const range = uploadTask["parseRange"]([""]);
assert.equal(range.minValue, -1);
assert.equal(range.maxValue, -1);
done();
});

it("Should return valid range for given range with from and to values", (done) => {
const range = uploadTask.parseRange(["100-200"]);
const range = uploadTask["parseRange"](["100-200"]);
assert.equal(range.minValue, 100);
assert.equal(range.maxValue, 200);
done();
});

it("Should return valid range for given range without to value", (done) => {
const range = uploadTask.parseRange(["0-"]);
const range = uploadTask["parseRange"](["0-"]);
assert.equal(range.minValue, 0);
assert.equal(range.maxValue, 99999);
done();
});
});
/* tslint:disable: no-string-literal */
describe("Update Task Status", () => {
const name = "sample_image.jpg";
const arrayBuffer = new ArrayBuffer(80000);
Expand All @@ -75,7 +75,7 @@ describe("LargeFileUploadTask.ts", () => {
expirationDateTime: "2018-08-06T09:05:45.195Z",
nextExpectedRanges: ["100-2000"],
};
uploadTask.updateTaskStatus(statusResponse);
uploadTask["updateTaskStatus"](statusResponse);
assert.equal(uploadTask["nextRange"].minValue, 100);
assert.equal(uploadTask["nextRange"].maxValue, 2000);
done();
Expand All @@ -85,13 +85,12 @@ describe("LargeFileUploadTask.ts", () => {
expirationDateTime: "2018-08-06T09:05:45.195Z",
nextExpectedRanges: ["100-"],
};
uploadTask.updateTaskStatus(statusResponse);
uploadTask["updateTaskStatus"](statusResponse);
assert.equal(uploadTask["nextRange"].minValue, 100);
assert.equal(uploadTask["nextRange"].maxValue, 99999);
done();
});
});
/* tslint:enable: no-string-literal */

describe("GetNextRange", () => {
const name = "sample_image.jpg";
Expand Down Expand Up @@ -123,7 +122,7 @@ describe("LargeFileUploadTask.ts", () => {
expirationDateTime: "2018-08-06T09:05:45.195Z",
nextExpectedRanges: ["327680-"],
};
uploadTask.updateTaskStatus(statusResponse);
uploadTask["updateTaskStatus"](statusResponse);
const nextRange = uploadTask.getNextRange();
assert.equal(nextRange.minValue, 327680);
assert.equal(nextRange.maxValue, 328679);
Expand All @@ -135,7 +134,7 @@ describe("LargeFileUploadTask.ts", () => {
expirationDateTime: "2018-08-06T09:05:45.195Z",
nextExpectedRanges: [],
};
uploadTask.updateTaskStatus(statusResponse);
uploadTask["updateTaskStatus"](statusResponse);
const nextRange = uploadTask.getNextRange();
assert.equal(nextRange.minValue, -1);
assert.equal(nextRange.maxValue, -1);
Expand Down Expand Up @@ -165,7 +164,7 @@ describe("LargeFileUploadTask.ts", () => {
expirationDateTime: "2018-08-06T09:05:45.195Z",
nextExpectedRanges: [],
};
uploadTask.updateTaskStatus(statusResponse);
uploadTask["updateTaskStatus"](statusResponse);
uploadTask
.upload()
.then((res) => {
Expand All @@ -178,4 +177,5 @@ describe("LargeFileUploadTask.ts", () => {
});
});
});
/* tslint:enable: no-string-literal */
});
16 changes: 9 additions & 7 deletions spec/tasks/OneDriveLargeFileUploadTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,34 @@ describe("OneDriveLargeFileUploadTask.ts", () => {
const specialFileName = "test file.png";
const encodedFileName = "test%20file.png";

/* tslint:disable: no-string-literal */
it("Should trim the extra spaces in the filename", () => {
assert.equal(`/me/drive/root:/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask.constructCreateSessionUrl(spaceFileName));
assert.equal(`/me/drive/root:/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask["constructCreateSessionUrl"](spaceFileName));
});

it("Should encode space in the filename", () => {
assert.equal(`/me/drive/root:/${encodedFileName}:/createUploadSession`, OneDriveLargeFileUploadTask.constructCreateSessionUrl(specialFileName));
assert.equal(`/me/drive/root:/${encodedFileName}:/createUploadSession`, OneDriveLargeFileUploadTask["constructCreateSessionUrl"](specialFileName));
});

it("Should return url with default root value", () => {
assert.equal(`/me/drive/root:/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask.constructCreateSessionUrl(fileName));
assert.equal(`/me/drive/root:/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask["constructCreateSessionUrl"](fileName));
});

it("Should return url with default root value for an empty path string", () => {
assert.equal(`/me/drive/root:/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask.constructCreateSessionUrl(fileName, ""));
assert.equal(`/me/drive/root:/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask["constructCreateSessionUrl"](fileName, ""));
});

it("Should add / in front of the path", () => {
assert.equal(`/me/drive/root:/Documents/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask.constructCreateSessionUrl(fileName, "Documents/"));
assert.equal(`/me/drive/root:/Documents/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask["constructCreateSessionUrl"](fileName, "Documents/"));
});

it("Should add / in back of the path", () => {
assert.equal(`/me/drive/root:/Documents/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask.constructCreateSessionUrl(fileName, "/Documents"));
assert.equal(`/me/drive/root:/Documents/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask["constructCreateSessionUrl"](fileName, "/Documents"));
});

it("Should trim the extra spaces in the path", () => {
assert.equal(`/me/drive/root:/Documents/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask.constructCreateSessionUrl(fileName, " /Documents/ "));
assert.equal(`/me/drive/root:/Documents/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask["constructCreateSessionUrl"](fileName, " /Documents/ "));
});
/* tslint:enable: no-string-literal */
});
});
1 change: 1 addition & 0 deletions src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export * from "../middleware/options/IMiddlewareOptions";
export * from "../middleware/options/RetryHandlerOptions";
export * from "../middleware/options/TelemetryHandlerOptions";

export * from "../tasks/LargeFileUploadTask";
export * from "../tasks/OneDriveLargeFileUploadTask";
export * from "../tasks/PageIterator";

Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export * from "./middleware/options/RetryHandlerOptions";
export * from "./middleware/options/RedirectHandlerOptions";
export * from "./middleware/options/TelemetryHandlerOptions";

export * from "./tasks/LargeFileUploadTask";
export * from "./tasks/OneDriveLargeFileUploadTask";
export * from "./tasks/PageIterator";

Expand Down
47 changes: 41 additions & 6 deletions src/tasks/LargeFileUploadTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@
import { Client } from "../index";
import { Range } from "../Range";

/**
* @interface
* Signature to representing key value pairs
* @property {[key: string] : string | number} - The Key value pair
*/
interface KeyValuePairObjectStringNumber {
[key: string]: string | number;
}

/**
* @interface
* Signature to represent the resulting response in the status enquiry request
* @property {string} expirationDateTime - The expiration of the time of the upload session
* @property {string} expirationDateTime - The expiration time of the upload session
* @property {string[]} nextExpectedRanges - The ranges expected in next consecutive request in the upload
*/
interface UploadStatusResponse {
Expand Down Expand Up @@ -97,6 +106,32 @@ export class LargeFileUploadTask {
*/
protected nextRange: Range;

/**
* @public
* @static
* @async
* Makes request to the server to create an upload session
* @param {Client} client - The GraphClient instance
* @param {any} payload - The payload that needs to be sent
* @param {KeyValuePairObjectStringNumber} headers - The headers that needs to be sent
* @returns The promise that resolves to LargeFileUploadSession
*/
public static async createUploadSession(client: Client, requestUrl: string, payload: any, headers: KeyValuePairObjectStringNumber = {}): Promise<any> {
try {
const session = await client
.api(requestUrl)
.headers(headers)
.post(payload);
const largeFileUploadSession: LargeFileUploadSession = {
url: session.uploadUrl,
expiry: new Date(session.expirationDateTime),
};
return largeFileUploadSession;
} catch (err) {
throw err;
}
}

/**
* @public
* @constructor
Expand All @@ -107,7 +142,7 @@ export class LargeFileUploadTask {
* @param {LargeFileUploadTaskOptions} options - The upload task options
* @returns An instance of LargeFileUploadTask
*/
public constructor(client: Client, file: FileObject, uploadSession: LargeFileUploadSession, options: LargeFileUploadTaskOptions) {
public constructor(client: Client, file: FileObject, uploadSession: LargeFileUploadSession, options: LargeFileUploadTaskOptions = {}) {
this.client = client;
this.file = file;
if (options.rangeSize === undefined) {
Expand All @@ -119,12 +154,12 @@ export class LargeFileUploadTask {
}

/**
* @public
* @private
* Parses given range string to the Range instance
* @param {string[]} ranges - The ranges value
* @returns The range instance
*/
public parseRange(ranges: string[]): Range {
private parseRange(ranges: string[]): Range {
const rangeStr = ranges[0];
if (typeof rangeStr === "undefined" || rangeStr === "") {
return new Range();
Expand All @@ -139,12 +174,12 @@ export class LargeFileUploadTask {
}

/**
* @public
* @private
* Updates the expiration date and the next range
* @param {UploadStatusResponse} response - The response of the upload status
* @returns Nothing
*/
public updateTaskStatus(response: UploadStatusResponse): void {
private updateTaskStatus(response: UploadStatusResponse): void {
this.uploadSession.expiry = new Date(response.expirationDateTime);
this.nextRange = this.parseRange(response.nextExpectedRanges);
}
Expand Down
Loading