Skip to content

Commit 4130614

Browse files
Merge pull request #162 from microsoftgraph/Large_File_Upload_For_SharePoint
Customing Large File Upload Task
2 parents 38fb4ee + 6f7ef2b commit 4130614

7 files changed

+157
-78
lines changed

docs/tasks/LargeFileUploadTask.md

+70-25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Large File Upload Task - Uploading large files to OneDrive.
1+
# Large File Upload Task - Uploading large files to OneDrive
22

3-
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).
3+
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).
44

55
## Creating the client instance
66

@@ -20,47 +20,60 @@ Get files from the input element and start uploading.
2020
async function fileUpload(elem) {
2121
let file = elem.files[0];
2222
try {
23-
let response = await largeFilUpload(client, file, file.name);
23+
let response = await largeFileUpload(client, file, file.name);
24+
console.log(response);
25+
console.log("File Uploaded Successfully.!!");
2426
} catch (error) {
2527
console.error(error);
2628
}
2729
}
30+
31+
async function largeFileUpload(client, file) {
32+
try {
33+
let options = {
34+
path: "/Documents",
35+
fileName: file.name,
36+
rangeSize: 1024 * 1024,
37+
};
38+
const uploadTask = await MicrosoftGraph.OneDriveLargeFileUploadTask.create(client, file, options);
39+
const response = await uploadTask.upload();
40+
return response;
41+
} catch (err) {
42+
throw err;
43+
}
44+
}
2845
```
2946

3047
## Uploading from NodeJS
3148

3249
```typescript
3350
function uploadFile() {
34-
fs.readFile(<PATH_OF_THE_FILE>, {}, function (err, file) {
35-
if(err) {
36-
throw err;
37-
}
38-
let fileName = <NAME_OF_THE_FILE>;
39-
largeFileUpload(client, file, fileName)
40-
.then((response) => {
41-
console.log(response);
42-
})
43-
.catch((error) => {
44-
console.error(error);
45-
});
46-
});
51+
fs.readFile("<PATH_OF_THE_FILE>", {}, function(err, file) {
52+
if (err) {
53+
throw err;
54+
}
55+
let fileName = "<NAME_OF_THE_FILE_WITH_EXTN>";
56+
oneDriveLargeFileUpload(client, file, fileName)
57+
.then((response) => {
58+
console.log(response);
59+
console.log("File Uploaded Successfully.!!");
60+
})
61+
.catch((error) => {
62+
throw err;
63+
});
64+
});
4765
}
48-
```
4966

50-
## Creating session and start uploading
51-
52-
```typescript
53-
async function uploadFile(client, file) {
67+
async function oneDriveLargeFileUpload(client, file, fileName) {
5468
try {
5569
let options = {
5670
path: "/Documents",
57-
fileName: file.name,
71+
fileName,
5872
rangeSize: 1024 * 1024,
5973
};
60-
const uploadTask = await MicrosoftGraph.OneDriveLargeFileUploadTask.create(client, file, options);
74+
const uploadTask = await OneDriveLargeFileUploadTask.create(client, file, options);
6175
const response = await uploadTask.upload();
62-
console.log(response);
63-
console.log("File Uploaded Successfully.!!");
76+
return response;
6477
} catch (err) {
6578
console.log(err);
6679
}
@@ -84,3 +97,35 @@ let range = uploadTask.getNextRange();
8497
let slicedFile = uploadTask.sliceFile(range);
8598
uploadTask.uploadSlice(slicedFile, range, uploadTask.file.size);
8699
```
100+
101+
## Uploading with custom options
102+
103+
_You can pass in the customized options using LargeFileUploadTask_
104+
105+
```typescript
106+
async function largeFileUpload(client, file) {
107+
const filename = file.name;
108+
const driveId = "<YOUR_DRIVE_ID>";
109+
const path = "LOCATION_TO_STORE_FILE";
110+
try {
111+
const requestUrl = `/drives/${driveId}/root:${path}/${fileName}:/createUploadSession`;
112+
const payload = {
113+
item: {
114+
"@microsoft.graph.conflictBehavior": "fail",
115+
name: fileName,
116+
},
117+
};
118+
const fileObject = {
119+
size: file.size,
120+
content: file,
121+
name: fileName,
122+
};
123+
const uploadSession = await LargeFileUploadTask.createUploadSession(client, requestUrl, payload);
124+
const uploadTask = await new LargeFileUploadTask(client, fileObject, uploadSession);
125+
const response = await uploadTask.upload();
126+
return response;
127+
} catch (err) {
128+
throw err;
129+
}
130+
}
131+
```

spec/tasks/LargeFileUploadTask.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { LargeFileUploadTask } from "../../src/tasks/LargeFileUploadTask";
1111
import { getClient } from "../test-helper";
1212

1313
describe("LargeFileUploadTask.ts", () => {
14+
/* tslint:disable: no-string-literal */
1415
describe("Parsing Range", () => {
1516
const name = "sample_image.jpg";
1617
const arrayBuffer = new ArrayBuffer(80000);
@@ -27,34 +28,33 @@ describe("LargeFileUploadTask.ts", () => {
2728
const options = {};
2829
const uploadTask = new LargeFileUploadTask(getClient(), fileObj, uploadSession, options);
2930
it("Should return default range for given undefined range", (done) => {
30-
const range = uploadTask.parseRange([]);
31+
const range = uploadTask["parseRange"]([]);
3132
assert.equal(range.minValue, -1);
3233
assert.equal(range.maxValue, -1);
3334
done();
3435
});
3536

3637
it("Should return default range for given empty range", (done) => {
37-
const range = uploadTask.parseRange([""]);
38+
const range = uploadTask["parseRange"]([""]);
3839
assert.equal(range.minValue, -1);
3940
assert.equal(range.maxValue, -1);
4041
done();
4142
});
4243

4344
it("Should return valid range for given range with from and to values", (done) => {
44-
const range = uploadTask.parseRange(["100-200"]);
45+
const range = uploadTask["parseRange"](["100-200"]);
4546
assert.equal(range.minValue, 100);
4647
assert.equal(range.maxValue, 200);
4748
done();
4849
});
4950

5051
it("Should return valid range for given range without to value", (done) => {
51-
const range = uploadTask.parseRange(["0-"]);
52+
const range = uploadTask["parseRange"](["0-"]);
5253
assert.equal(range.minValue, 0);
5354
assert.equal(range.maxValue, 99999);
5455
done();
5556
});
5657
});
57-
/* tslint:disable: no-string-literal */
5858
describe("Update Task Status", () => {
5959
const name = "sample_image.jpg";
6060
const arrayBuffer = new ArrayBuffer(80000);
@@ -75,7 +75,7 @@ describe("LargeFileUploadTask.ts", () => {
7575
expirationDateTime: "2018-08-06T09:05:45.195Z",
7676
nextExpectedRanges: ["100-2000"],
7777
};
78-
uploadTask.updateTaskStatus(statusResponse);
78+
uploadTask["updateTaskStatus"](statusResponse);
7979
assert.equal(uploadTask["nextRange"].minValue, 100);
8080
assert.equal(uploadTask["nextRange"].maxValue, 2000);
8181
done();
@@ -85,13 +85,12 @@ describe("LargeFileUploadTask.ts", () => {
8585
expirationDateTime: "2018-08-06T09:05:45.195Z",
8686
nextExpectedRanges: ["100-"],
8787
};
88-
uploadTask.updateTaskStatus(statusResponse);
88+
uploadTask["updateTaskStatus"](statusResponse);
8989
assert.equal(uploadTask["nextRange"].minValue, 100);
9090
assert.equal(uploadTask["nextRange"].maxValue, 99999);
9191
done();
9292
});
9393
});
94-
/* tslint:enable: no-string-literal */
9594

9695
describe("GetNextRange", () => {
9796
const name = "sample_image.jpg";
@@ -123,7 +122,7 @@ describe("LargeFileUploadTask.ts", () => {
123122
expirationDateTime: "2018-08-06T09:05:45.195Z",
124123
nextExpectedRanges: ["327680-"],
125124
};
126-
uploadTask.updateTaskStatus(statusResponse);
125+
uploadTask["updateTaskStatus"](statusResponse);
127126
const nextRange = uploadTask.getNextRange();
128127
assert.equal(nextRange.minValue, 327680);
129128
assert.equal(nextRange.maxValue, 328679);
@@ -135,7 +134,7 @@ describe("LargeFileUploadTask.ts", () => {
135134
expirationDateTime: "2018-08-06T09:05:45.195Z",
136135
nextExpectedRanges: [],
137136
};
138-
uploadTask.updateTaskStatus(statusResponse);
137+
uploadTask["updateTaskStatus"](statusResponse);
139138
const nextRange = uploadTask.getNextRange();
140139
assert.equal(nextRange.minValue, -1);
141140
assert.equal(nextRange.maxValue, -1);
@@ -165,7 +164,7 @@ describe("LargeFileUploadTask.ts", () => {
165164
expirationDateTime: "2018-08-06T09:05:45.195Z",
166165
nextExpectedRanges: [],
167166
};
168-
uploadTask.updateTaskStatus(statusResponse);
167+
uploadTask["updateTaskStatus"](statusResponse);
169168
uploadTask
170169
.upload()
171170
.then((res) => {
@@ -178,4 +177,5 @@ describe("LargeFileUploadTask.ts", () => {
178177
});
179178
});
180179
});
180+
/* tslint:enable: no-string-literal */
181181
});

spec/tasks/OneDriveLargeFileUploadTask.ts

+9-7
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,34 @@ describe("OneDriveLargeFileUploadTask.ts", () => {
1616
const specialFileName = "test file.png";
1717
const encodedFileName = "test%20file.png";
1818

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

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

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

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

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

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

4344
it("Should trim the extra spaces in the path", () => {
44-
assert.equal(`/me/drive/root:/Documents/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask.constructCreateSessionUrl(fileName, " /Documents/ "));
45+
assert.equal(`/me/drive/root:/Documents/${fileName}:/createUploadSession`, OneDriveLargeFileUploadTask["constructCreateSessionUrl"](fileName, " /Documents/ "));
4546
});
47+
/* tslint:enable: no-string-literal */
4648
});
4749
});

src/browser/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export * from "../middleware/options/IMiddlewareOptions";
1919
export * from "../middleware/options/RetryHandlerOptions";
2020
export * from "../middleware/options/TelemetryHandlerOptions";
2121

22+
export * from "../tasks/LargeFileUploadTask";
2223
export * from "../tasks/OneDriveLargeFileUploadTask";
2324
export * from "../tasks/PageIterator";
2425

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export * from "./middleware/options/RetryHandlerOptions";
2323
export * from "./middleware/options/RedirectHandlerOptions";
2424
export * from "./middleware/options/TelemetryHandlerOptions";
2525

26+
export * from "./tasks/LargeFileUploadTask";
2627
export * from "./tasks/OneDriveLargeFileUploadTask";
2728
export * from "./tasks/PageIterator";
2829

src/tasks/LargeFileUploadTask.ts

+41-6
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,19 @@
1212
import { Client } from "../index";
1313
import { Range } from "../Range";
1414

15+
/**
16+
* @interface
17+
* Signature to representing key value pairs
18+
* @property {[key: string] : string | number} - The Key value pair
19+
*/
20+
interface KeyValuePairObjectStringNumber {
21+
[key: string]: string | number;
22+
}
23+
1524
/**
1625
* @interface
1726
* Signature to represent the resulting response in the status enquiry request
18-
* @property {string} expirationDateTime - The expiration of the time of the upload session
27+
* @property {string} expirationDateTime - The expiration time of the upload session
1928
* @property {string[]} nextExpectedRanges - The ranges expected in next consecutive request in the upload
2029
*/
2130
interface UploadStatusResponse {
@@ -97,6 +106,32 @@ export class LargeFileUploadTask {
97106
*/
98107
protected nextRange: Range;
99108

109+
/**
110+
* @public
111+
* @static
112+
* @async
113+
* Makes request to the server to create an upload session
114+
* @param {Client} client - The GraphClient instance
115+
* @param {any} payload - The payload that needs to be sent
116+
* @param {KeyValuePairObjectStringNumber} headers - The headers that needs to be sent
117+
* @returns The promise that resolves to LargeFileUploadSession
118+
*/
119+
public static async createUploadSession(client: Client, requestUrl: string, payload: any, headers: KeyValuePairObjectStringNumber = {}): Promise<any> {
120+
try {
121+
const session = await client
122+
.api(requestUrl)
123+
.headers(headers)
124+
.post(payload);
125+
const largeFileUploadSession: LargeFileUploadSession = {
126+
url: session.uploadUrl,
127+
expiry: new Date(session.expirationDateTime),
128+
};
129+
return largeFileUploadSession;
130+
} catch (err) {
131+
throw err;
132+
}
133+
}
134+
100135
/**
101136
* @public
102137
* @constructor
@@ -107,7 +142,7 @@ export class LargeFileUploadTask {
107142
* @param {LargeFileUploadTaskOptions} options - The upload task options
108143
* @returns An instance of LargeFileUploadTask
109144
*/
110-
public constructor(client: Client, file: FileObject, uploadSession: LargeFileUploadSession, options: LargeFileUploadTaskOptions) {
145+
public constructor(client: Client, file: FileObject, uploadSession: LargeFileUploadSession, options: LargeFileUploadTaskOptions = {}) {
111146
this.client = client;
112147
this.file = file;
113148
if (options.rangeSize === undefined) {
@@ -119,12 +154,12 @@ export class LargeFileUploadTask {
119154
}
120155

121156
/**
122-
* @public
157+
* @private
123158
* Parses given range string to the Range instance
124159
* @param {string[]} ranges - The ranges value
125160
* @returns The range instance
126161
*/
127-
public parseRange(ranges: string[]): Range {
162+
private parseRange(ranges: string[]): Range {
128163
const rangeStr = ranges[0];
129164
if (typeof rangeStr === "undefined" || rangeStr === "") {
130165
return new Range();
@@ -139,12 +174,12 @@ export class LargeFileUploadTask {
139174
}
140175

141176
/**
142-
* @public
177+
* @private
143178
* Updates the expiration date and the next range
144179
* @param {UploadStatusResponse} response - The response of the upload status
145180
* @returns Nothing
146181
*/
147-
public updateTaskStatus(response: UploadStatusResponse): void {
182+
private updateTaskStatus(response: UploadStatusResponse): void {
148183
this.uploadSession.expiry = new Date(response.expirationDateTime);
149184
this.nextRange = this.parseRange(response.nextExpectedRanges);
150185
}

0 commit comments

Comments
 (0)