Skip to content

Commit

Permalink
Fix multipart upload - move query parameters to HTTP request query ob…
Browse files Browse the repository at this point in the history
…ject (#47)

* minor correction of the uploads parameter
* fixed protocol to be `this.scheme`
* add @oleiade's example
  • Loading branch information
immavalls authored May 9, 2023
1 parent 4b52e0b commit adab2d4
Show file tree
Hide file tree
Showing 13 changed files with 99 additions and 26 deletions.
2 changes: 1 addition & 1 deletion build/aws.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/aws.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/index.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/kms.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/s3.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/s3.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/secrets-manager.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/signature.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/sqs.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/ssm.js.map

Large diffs are not rendered by default.

66 changes: 66 additions & 0 deletions examples/s3-multipart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import crypto from 'k6/crypto'
import exec from 'k6/execution'

import { AWSConfig, S3Client } from '../build/s3.js'

const awsConfig = new AWSConfig({
region: __ENV.AWS_REGION,
accessKeyId: __ENV.AWS_ACCESS_KEY_ID,
secretAccessKey: __ENV.AWS_SECRET_ACCESS_KEY,
sessionToken: __ENV.AWS_SESSION_TOKEN,
})


const s3 = new S3Client(awsConfig)

const testBucketName = 'test-jslib-aws'
const testFileKey = 'multipart.txt'

export default function () {
// List the buckets the AWS authentication configuration
// gives us access to.
const buckets = s3.listBuckets()

// If our test bucket does not exist, abort the execution.
if (buckets.filter((b) => b.name === testBucketName).length == 0) {
exec.test.abort()
}

// Produce random bytes to upload of size ~12MB, that
// we will upload in two 6MB parts. This is done as the
// minimum part size supported by S3 is 5MB.
const bigFile = crypto.randomBytes(12 * 1024 * 1024)

// Initialize a multipart upload
const multipartUpload = s3.createMultipartUpload(testBucketName, testFileKey)

// Upload the first part
const firstPartData = bigFile.slice(0, 6 * 1024 * 1024)
const firstPart = s3.uploadPart(
testBucketName,
testFileKey,
multipartUpload.uploadId,
1,
firstPartData
)

// Upload the second part
const secondPartData = bigFile.slice(6 * 1024 * 1024, 12 * 1024 * 1024)
const secondPart = s3.uploadPart(
testBucketName,
testFileKey,
multipartUpload.uploadId,
2,
secondPartData
)

// Complete the multipart upload
s3.completeMultipartUpload(testBucketName, testFileKey, multipartUpload.uploadId, [
firstPart,
secondPart,
])

// Let's redownload it verify it's correct, and delete it
const obj = s3.getObject(testBucketName, testFileKey)
s3.deleteObject(testBucketName, testFileKey)
}
37 changes: 22 additions & 15 deletions src/internal/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ export class S3Client extends AWSClient {
// Prepare request
const method = 'PUT'
const host = `${this.host}`

const signedRequest = this.signature.sign(
{
method: method,
Expand Down Expand Up @@ -274,7 +274,7 @@ export class S3Client extends AWSClient {
* The uploadId returned can be used to upload parts to the object.
*
* @param {string} bucketName - The bucket name containing the object.
* @param {string} objectKey - Key of the object to delete.
* @param {string} objectKey - Key of the object to upload.
* @return {S3MultipartUpload} - returns the uploadId of the newly created multipart upload.
* @throws {S3ServiceError}
* @throws {InvalidSignatureError}
Expand All @@ -283,15 +283,15 @@ export class S3Client extends AWSClient {
// Prepare request
const method = 'POST'
const host = `${bucketName}.${this.host}`
const query = 'uploads'

const signedRequest = this.signature.sign(
{
method: method,
protocol: 'https',
protocol: this.scheme,
hostname: host,
path: `/${objectKey}?${query}`,
path: `/${objectKey}`,
headers: {},
query: { uploads: '' },
},
{}
)
Expand All @@ -312,7 +312,7 @@ export class S3Client extends AWSClient {
/**
* Uploads a part in a multipart upload.
* @param {string} bucketName - The bucket name containing the object.
* @param {string} objectKey - Key of the object to delete.
* @param {string} objectKey - Key of the object to upload.
* @param {string} uploadId - The uploadId of the multipart upload.
* @param {number} partNumber - The part number of the part to upload.
* @param {string | ArrayBuffer} data - The content of the part to upload.
Expand All @@ -329,15 +329,18 @@ export class S3Client extends AWSClient {
// Prepare request
const method = 'PUT'
const host = `${bucketName}.${this.host}`
const query = `partNumber=${partNumber}&uploadId=${uploadId}`
const signedRequest = this.signature.sign(
{
method: method,
protocol: 'https',
protocol: this.scheme,
hostname: host,
path: `/${objectKey}?${query}`,
path: `/${objectKey}`,
headers: {},
body: data,
query: {
partNumber: `${partNumber}`,
uploadId: `${uploadId}`,
},
},
{}
)
Expand Down Expand Up @@ -369,7 +372,6 @@ export class S3Client extends AWSClient {
// Prepare request
const method = 'POST'
const host = `${bucketName}.${this.host}`
const query = `uploadId=${uploadId}`
const body = `<CompleteMultipartUpload>${parts
.map(
(part) =>
Expand All @@ -379,11 +381,14 @@ export class S3Client extends AWSClient {
const signedRequest = this.signature.sign(
{
method: method,
protocol: 'https',
protocol: this.scheme,
hostname: host,
path: `/${objectKey}?${query}`,
path: `/${objectKey}`,
headers: {},
body: body,
query: {
uploadId: `${uploadId}`,
},
},
{}
)
Expand All @@ -408,14 +413,16 @@ export class S3Client extends AWSClient {
// Prepare request
const method = 'DELETE'
const host = `${bucketName}.${this.host}`
const query = `uploadId=${uploadId}`
const signedRequest = this.signature.sign(
{
method: method,
protocol: 'https',
protocol: this.scheme,
hostname: host,
path: `/${objectKey}?${query}`,
path: `/${objectKey}`,
headers: {},
query: {
uploadId: `${uploadId}`,
},
},
{}
)
Expand Down

0 comments on commit adab2d4

Please sign in to comment.