Skip to content

Commit 2bd8f6a

Browse files
alexforsythtrivikr
andauthored
feat(lib-storage): rewrite lib-storage upload (#2039)
* fix: proper dependencies and node/browser configuration * feat: adding example code for upload usage * feat: export upload and upload types * fix: example code imports the @aws-sdk/lib-storage package * add tests for upload * feat: rewrite functions that get chunks of buffers, readable, and readableStreams * feat: add chunker wrapper that properly sends data to the correct chunker * feat: add upload types * feat: add main uploader that manages multipart upload steps * feat: add best attempt at getting the byte size of an object * fix: remove old files as part of rewrite * Fix: add support for tags * fix: only concat buffers when there is more than one buffer * fix: grammatical and naming fixes to address feedback * fix: correct the max number of parts s3 allows for multipart upload to 10000 * fix: use const in place of let * fix: calling abort should throw abort error from upload Co-authored-by: Trivikram Kamat <[email protected]> * fix: fully enumerate upload options on readme * fix: throw error on abort * fix: addressing PR feedback * fix: manually ran prettier * fix: nit single line branch * fix: remove unneeded add * Fix: PR feedback Co-authored-by: Trivikram Kamat <[email protected]>
1 parent 6e562ba commit 2bd8f6a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1565
-5169
lines changed

lib/storage/README.md

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
# @aws-sdk/lib-storage
22

3-
[![NPM version](https://img.shields.io/npm/v/@aws-sdk/lib-storage/latest.svg)](https://www.npmjs.com/package/@aws-sdk/abort-controller)
4-
[![NPM downloads](https://img.shields.io/npm/dm/@aws-sdk/lib-storage.svg)](https://www.npmjs.com/package/@aws-sdk/abort-controller)
3+
[![NPM version](https://img.shields.io/npm/v/@aws-sdk/lib-storage/latest.svg)](https://www.npmjs.com/package/@aws-sdk/lib-storage)
4+
[![NPM downloads](https://img.shields.io/npm/dm/@aws-sdk/lib-storage.svg)](https://www.npmjs.com/package/@aws-sdk/lib-storage)
55

66
### Upload
77

88
Upload allows for easy and efficient uploading of buffers, blobs, or streams, using a configurable amount of concurrency to perform multipart uploads where possible. This abstraction enables uploading large files or streams of unknown size due to the use of multipart uploads under the hood.
99

1010
```
1111
import { Upload } from "@aws-sdk/lib-storage";
12+
import { S3Client, S3 } from "@aws-sdk/client-s3";
13+
1214
const target = { Bucket, Key, Body };
1315
try {
1416
const paralellUploads3 = new Upload({
15-
client: new S3({}),
17+
client: new S3({}) || new S3Client({}),
18+
tags: [...], // optional tags
19+
queueSize: 4, // optional concurrency configuration
20+
partSize: 5MB, // optional size of each part
21+
leavePartsOnError: false, // optional manually handle dropped parts
1622
params: target,
1723
});
1824

lib/storage/example-code/config.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export const configuration = {
2+
Bucket: "test-bucket-1234567890",
3+
Key: `unique-key-${Date.now}`,
4+
};
+16-21
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,24 @@
1-
import { Upload } from "../src/index";
1+
import { Upload } from "@aws-sdk/lib-storage";
22
import { S3 } from "@aws-sdk/client-s3";
3+
import { configuration } from "./config";
34

45
const fs = require("fs");
5-
const fileStream = fs.createReadStream("./big.file");
6+
const fileStream = fs.createReadStream(__dirname + "/big.file");
67

78
(async () => {
8-
const target = {
9-
Bucket: "aws-sdk-js-mock-files",
10-
Key: "data_key",
11-
Body: fileStream,
12-
};
9+
const upload = new Upload({
10+
params: {
11+
Bucket: configuration.Bucket,
12+
Key: configuration.Key,
13+
Body: fileStream,
14+
},
15+
client: new S3({}),
16+
queueSize: 3,
17+
});
1318

14-
try {
15-
const upload = new Upload({
16-
params: target,
17-
client: new S3({}),
18-
queueSize: 3,
19-
});
19+
upload.on("httpUploadProgress", (progress) => {
20+
console.log(progress);
21+
});
2022

21-
upload.on("httpUploadProgress", (progress) => {
22-
console.log(progress);
23-
});
24-
25-
await upload.done();
26-
} catch (e) {
27-
console.log(e);
28-
}
23+
await upload.done();
2924
})();
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { S3Client } from "@aws-sdk/client-s3";
2+
import { Upload } from "@aws-sdk/lib-storage";
3+
import { configuration } from "./config";
4+
import { Readable } from "stream";
5+
6+
const Bucket = configuration.Bucket;
7+
const region = "us-west-2";
8+
9+
const sleep = async (seconds: number) => {
10+
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
11+
};
12+
13+
async function* generateContents() {
14+
for (let index = 0; index < 8; index++) {
15+
const time = Math.random() * 5;
16+
await sleep(time);
17+
console.log(`Delaying part ${index} for ${time}`);
18+
yield `[Part ${index}] ${"#".repeat(2000000)}`;
19+
}
20+
}
21+
22+
const uploadIndeterminateLengthStreamNode = async () => {
23+
const streamOfUnknownlength = Readable.from(generateContents());
24+
25+
const Key = configuration.Key;
26+
let upload = new Upload({
27+
client: new S3Client({ region }),
28+
params: {
29+
Key,
30+
Bucket,
31+
Body: streamOfUnknownlength,
32+
},
33+
});
34+
35+
upload.on("httpUploadProgress", (progress: ProgressEvent) => {
36+
console.log(progress);
37+
});
38+
39+
setTimeout(() => {
40+
console.log(" Aborting ....");
41+
let res = upload.abort();
42+
}, 10 * 1000);
43+
44+
const uploadResult = await upload.done();
45+
console.log("done uploading", uploadResult);
46+
};
47+
48+
uploadIndeterminateLengthStreamNode();
+13-31
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,22 @@
1-
import { Upload } from "../src/index";
2-
import { S3, S3Client } from "@aws-sdk/client-s3";
1+
import { Upload } from "@aws-sdk/lib-storage";
2+
import { S3 } from "@aws-sdk/client-s3";
3+
import { configuration } from "./config";
34

4-
const Bucket = "aws-sdk-js-mock-files";
5-
const Key = "data_key";
5+
const Bucket = configuration.Bucket;
6+
const Key = configuration.Key;
67
const Body =
78
"Duo Reges: constructio interrete. Qui autem esse poteris, nisi te amor ipse ceperit? Hoc est non modo cor non habere, sed ne palatum quidem. Quantam rem agas, ut Circeis qui habitet totum hunc mundum suum municipium esse existimet? Huius, Lyco, oratione locuples, rebus ipsis ielunior. Sed quid attinet de rebus tam apertis plura requirere? Non quam nostram quidem, inquit Pomponius iocans; Alterum significari idem, ut si diceretur, officia media omnia aut pleraque servantem vivere. Stoici scilicet";
89

910
(async () => {
1011
const target = { Bucket, Key, Body };
11-
try {
12-
const paralellUploads3 = new Upload({
13-
client: new S3({}),
14-
params: target,
15-
});
12+
const paralellUploads3 = new Upload({
13+
client: new S3({}),
14+
params: target,
15+
});
1616

17-
paralellUploads3.on("httpUploadProgress", (progress) => {
18-
console.log(progress);
19-
});
17+
paralellUploads3.on("httpUploadProgress", (progress) => {
18+
console.log(progress);
19+
});
2020

21-
await paralellUploads3.done();
22-
} catch (e) {
23-
console.log(e);
24-
}
25-
26-
try {
27-
const paralellUploads3Client = new Upload({
28-
client: new S3Client({}),
29-
params: target,
30-
});
31-
32-
paralellUploads3Client.on("httpUploadProgress", (progress) => {
33-
console.log(progress);
34-
});
35-
36-
await paralellUploads3Client.done();
37-
} catch (e) {
38-
console.log(e);
39-
}
21+
await paralellUploads3.done();
4022
})();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { fromCognitoIdentityPool } from "@aws-sdk/credential-provider-cognito-identity";
2+
import { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity";
3+
4+
import { S3Client } from "@aws-sdk/client-s3";
5+
import { Upload } from "@aws-sdk/lib-storage";
6+
7+
import { configuration } from "./config";
8+
9+
const idPool = "us-west-2:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
10+
const Bucket = configuration.Bucket;
11+
const region = "us-west-2";
12+
13+
const sleep = async (seconds: number) => {
14+
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
15+
};
16+
17+
const fakeStreamOfUnknownlength = new ReadableStream({
18+
start: async (controller) => {
19+
for (let index = 0; index < 8; index++) {
20+
const time = Math.random() * 5;
21+
await sleep(time);
22+
console.log(`Delaying part ${index} for ${time}`);
23+
controller.enqueue(`[part ${index}] with ${"#".repeat(2000000)}`);
24+
}
25+
controller.close();
26+
},
27+
});
28+
29+
const uploadIndeterminateLengthStreamBrowser = async () => {
30+
const client = new S3Client({
31+
region,
32+
credentials: fromCognitoIdentityPool({
33+
client: new CognitoIdentityClient({ region }),
34+
identityPoolId: idPool,
35+
}),
36+
});
37+
38+
const Key = configuration.Key;
39+
40+
let upload = new Upload({
41+
client,
42+
params: {
43+
Key,
44+
Bucket,
45+
Body: fakeStreamOfUnknownlength,
46+
},
47+
});
48+
49+
upload.on("httpUploadProgress", (progress: ProgressEvent) => {
50+
console.log(progress);
51+
});
52+
53+
const uploadResult = await upload.done();
54+
console.log("done uploading", uploadResult);
55+
};
56+
57+
uploadIndeterminateLengthStreamBrowser();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { S3Client } from "@aws-sdk/client-s3";
2+
import { Upload } from "@aws-sdk/lib-storage";
3+
4+
import { Readable } from "stream";
5+
import { configuration } from "./config";
6+
7+
const Bucket = configuration.Bucket;
8+
const region = "us-west-2";
9+
10+
const sleep = async (seconds: number) => {
11+
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
12+
};
13+
14+
async function* generateContents() {
15+
for (let index = 0; index < 8; index++) {
16+
const time = Math.random() * 10;
17+
await sleep(time);
18+
console.log(`Delaying part ${index} for ${time}`);
19+
yield `[Part ${index}] ${"#".repeat(2000000)}`;
20+
}
21+
}
22+
const fakeStreamOfUnknownlength = Readable.from(generateContents());
23+
24+
const uploadIndeterminateLengthStreamNode = async () => {
25+
const Key = configuration.Key;
26+
let upload = new Upload({
27+
client: new S3Client({ region }),
28+
params: {
29+
Key,
30+
Bucket,
31+
Body: fakeStreamOfUnknownlength,
32+
},
33+
});
34+
35+
upload.on("httpUploadProgress", (progress: ProgressEvent) => {
36+
console.log(progress);
37+
});
38+
39+
const uploadResult = await upload.done();
40+
console.log("done uploading", uploadResult);
41+
};
42+
43+
uploadIndeterminateLengthStreamNode();

lib/storage/package.json

+12-3
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@
2323
},
2424
"license": "Apache-2.0",
2525
"dependencies": {
26+
"@aws-sdk/abort-controller": "3.4.1",
2627
"@aws-sdk/client-s3": "3.5.0",
2728
"buffer": "^5.6.0",
2829
"stream-browserify": "^3.0.0",
29-
"tslib": "^1.8.0",
30-
"web-streams-polyfill": "^3.0.0"
30+
"tslib": "^1.8.0"
3131
},
3232
"devDependencies": {
3333
"@types/jest": "^26.0.4",
@@ -40,7 +40,8 @@
4040
"karma-spec-reporter": "^0.0.32",
4141
"karma-typescript": "^5.2.0",
4242
"ts-jest": "^26.4.1",
43-
"typescript": "~4.1.2"
43+
"typescript": "~4.1.2",
44+
"web-streams-polyfill": "^3.0.0"
4445
},
4546
"typesVersions": {
4647
"<4.0": {
@@ -49,6 +50,14 @@
4950
]
5051
}
5152
},
53+
"browser": {
54+
"stream": "stream-browserify",
55+
"fs": "./src/runtimeConfig.browser",
56+
"./runtimeConfig": "./src/runtimeConfig.browser"
57+
},
58+
"react-native": {
59+
"./runtimeConfig": "./src/runtimeConfig.native"
60+
},
5261
"homepage": "https://github.com/trivikr/aws-sdk-js-v3/tree/master/lib/storage",
5362
"repository": {
5463
"type": "git",

0 commit comments

Comments
 (0)