Skip to content

Commit 81b764d

Browse files
authored
feat: Add support for file upload via Readable stream (#459)
1 parent 1867b17 commit 81b764d

6 files changed

Lines changed: 623 additions & 108 deletions

File tree

index.js

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const {
1111
HeadBucketCommand,
1212
} = require('@aws-sdk/client-s3');
1313
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
14+
const { Upload } = require('@aws-sdk/lib-storage');
1415
const optionsFromArguments = require('./lib/optionsFromArguments');
1516

1617
const awsCredentialsDeprecationNotice = function awsCredentialsDeprecationNotice() {
@@ -108,6 +109,10 @@ class S3Adapter {
108109
this._hasBucket = false;
109110
}
110111

112+
get supportsStreaming() {
113+
return true;
114+
}
115+
111116
async createBucket() {
112117
if (this._hasBucket) {
113118
return;
@@ -138,15 +143,12 @@ class S3Adapter {
138143
}
139144
}
140145

141-
// For a given config object, filename, and data, store a file in S3
142-
// Returns a promise containing the S3 object creation response
143-
async createFile(filename, data, contentType, options = {}) {
146+
_buildCreateFileParams(filename, data, contentType, options = {}) {
144147
const params = {
145148
Bucket: this._bucket,
146149
Key: this._bucketPrefix + filename,
147150
Body: data,
148151
};
149-
150152
if (this._generateKey instanceof Function) {
151153
params.Key = this._bucketPrefix + this._generateKey(filename);
152154
}
@@ -172,16 +174,39 @@ class S3Adapter {
172174
params.Metadata = options.metadata;
173175
}
174176
if (options.tags && typeof options.tags === 'object') {
175-
const serializedTags = serialize(options.tags);
176-
params.Tagging = serializedTags;
177+
params.Tagging = serialize(options.tags);
177178
}
179+
return params;
180+
}
181+
182+
// For a given config object, filename, and data, store a file in S3
183+
// Returns a promise containing the S3 object creation response
184+
async createFile(filename, data, contentType, options = {}) {
185+
const params = this._buildCreateFileParams(filename, data, contentType, options);
186+
const endpoint = this._endpoint || `https://${this._bucket}.s3.${this._region}.amazonaws.com`;
187+
188+
// Streaming upload path
189+
if (typeof data?.pipe === 'function') {
190+
const upload = new Upload({ client: this._s3Client, params });
191+
return new Promise((resolve, reject) => {
192+
data.on('error', (err) => {
193+
upload.abort().catch(() => {});
194+
reject(err);
195+
});
196+
this.createBucket()
197+
.then(() => upload.done())
198+
.then(
199+
(response) => resolve(Object.assign(response || {}, { Location: `${endpoint}/${params.Key}` })),
200+
reject
201+
);
202+
});
203+
}
204+
205+
// Buffer upload path
178206
await this.createBucket();
179207
const command = new PutObjectCommand(params);
180208
const response = await this._s3Client.send(command);
181-
const endpoint = this._endpoint || `https://${this._bucket}.s3.${this._region}.amazonaws.com`;
182-
const location = `${endpoint}/${params.Key}`;
183-
184-
return Object.assign(response || {}, { Location: location });
209+
return Object.assign(response || {}, { Location: `${endpoint}/${params.Key}` });
185210
}
186211

187212
async deleteFile(filename) {

0 commit comments

Comments
 (0)