@allegria/aws-file-manager 0.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -18
- package/dist/cjs/aws-file-manager.d.ts +318 -0
- package/dist/cjs/aws-file-manager.js +422 -0
- package/dist/cjs/aws-file-manager.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/examples/01-setup.d.ts +14 -0
- package/dist/examples/01-setup.js +42 -0
- package/dist/examples/01-setup.js.map +1 -0
- package/dist/examples/02-upload-multer.d.ts +13 -0
- package/dist/examples/02-upload-multer.js +63 -0
- package/dist/examples/02-upload-multer.js.map +1 -0
- package/dist/examples/03-upload-web.d.ts +12 -0
- package/dist/examples/03-upload-web.js +38 -0
- package/dist/examples/03-upload-web.js.map +1 -0
- package/dist/examples/04-signed-urls.d.ts +12 -0
- package/dist/examples/04-signed-urls.js +48 -0
- package/dist/examples/04-signed-urls.js.map +1 -0
- package/dist/examples/05-download.d.ts +14 -0
- package/dist/examples/05-download.js +53 -0
- package/dist/examples/05-download.js.map +1 -0
- package/dist/examples/06-delete.d.ts +17 -0
- package/dist/examples/06-delete.js +44 -0
- package/dist/examples/06-delete.js.map +1 -0
- package/dist/examples/07-copy.d.ts +14 -0
- package/dist/examples/07-copy.js +35 -0
- package/dist/examples/07-copy.js.map +1 -0
- package/dist/examples/08-list-paginated.d.ts +16 -0
- package/dist/examples/08-list-paginated.js +60 -0
- package/dist/examples/08-list-paginated.js.map +1 -0
- package/dist/examples/09-exists.d.ts +13 -0
- package/dist/examples/09-exists.js +32 -0
- package/dist/examples/09-exists.js.map +1 -0
- package/dist/lib/aws-file-manager.d.ts +262 -102
- package/dist/lib/aws-file-manager.js +353 -118
- package/dist/lib/aws-file-manager.js.map +1 -1
- package/dist/tests/aws-file-manager.test.d.ts +1 -0
- package/dist/tests/aws-file-manager.test.js +359 -0
- package/dist/tests/aws-file-manager.test.js.map +1 -0
- package/examples/01-setup.ts +49 -0
- package/examples/02-upload-multer.ts +82 -0
- package/examples/03-upload-web.ts +46 -0
- package/examples/04-signed-urls.ts +55 -0
- package/examples/05-download.ts +64 -0
- package/examples/06-delete.ts +53 -0
- package/examples/07-copy.ts +47 -0
- package/examples/08-list-paginated.ts +71 -0
- package/examples/09-exists.ts +39 -0
- package/package.json +36 -9
|
@@ -1,130 +1,149 @@
|
|
|
1
|
-
import { S3Client, PutObjectCommand, GetObjectCommand, StorageClass, } from "@aws-sdk/client-s3";
|
|
1
|
+
import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, CopyObjectCommand, ListObjectsV2Command, StorageClass, } from "@aws-sdk/client-s3";
|
|
2
2
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Adapter helpers — normalise caller-side file representations
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
3
7
|
/**
|
|
4
|
-
*
|
|
8
|
+
* Adapts a multer file (Express.Multer.File) to FileInput.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // In an Express route with multer:
|
|
12
|
+
* const fileInput = fromMulterFile(req.file);
|
|
5
13
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
export function fromMulterFile(multerFile) {
|
|
15
|
+
return {
|
|
16
|
+
buffer: multerFile.buffer,
|
|
17
|
+
originalName: multerFile.originalname,
|
|
18
|
+
mimeType: multerFile.mimetype,
|
|
19
|
+
size: multerFile.size,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Adapts a Web API File (browser / Next.js App Router FormData) to FileInput.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // In a Next.js App Router route handler:
|
|
27
|
+
* const formData = await request.formData();
|
|
28
|
+
* const fileInput = await fromWebFile(formData.get('file') as File);
|
|
29
|
+
*/
|
|
30
|
+
export async function fromWebFile(webFile) {
|
|
31
|
+
return {
|
|
32
|
+
buffer: Buffer.from(await webFile.arrayBuffer()),
|
|
33
|
+
originalName: webFile.name,
|
|
34
|
+
mimeType: webFile.type,
|
|
35
|
+
size: webFile.size,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Main class
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
export class AwsFileManager {
|
|
11
42
|
constructor(config) {
|
|
12
|
-
// Validate required config
|
|
13
43
|
if (!config.region)
|
|
14
44
|
throw new Error("AWS region is required");
|
|
15
45
|
if (!config.bucketName)
|
|
16
46
|
throw new Error("S3 bucket name is required");
|
|
17
|
-
|
|
18
|
-
this.acl = config.acl || "private";
|
|
47
|
+
this.bucketName = config.bucketName;
|
|
19
48
|
this.basePath = config.basePath
|
|
20
49
|
? config.basePath.replace(/^\/|\/$/g, "")
|
|
21
50
|
: "";
|
|
22
|
-
this.bucketName = config.bucketName;
|
|
23
51
|
this.urlExpirationSeconds =
|
|
24
|
-
config.urlExpirationSeconds
|
|
52
|
+
config.urlExpirationSeconds ??
|
|
25
53
|
AwsFileManager.DEFAULTS.urlExpirationSeconds;
|
|
26
54
|
this.storageClass =
|
|
27
|
-
config.storageClass
|
|
28
|
-
|
|
29
|
-
const s3ClientOptions = {
|
|
55
|
+
config.storageClass ?? AwsFileManager.DEFAULTS.storageClass;
|
|
56
|
+
this.s3 = new S3Client({
|
|
30
57
|
region: config.region,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
this.s3Client = new S3Client(s3ClientOptions);
|
|
58
|
+
...(config.accessKeyId &&
|
|
59
|
+
config.secretAccessKey && {
|
|
60
|
+
credentials: {
|
|
61
|
+
accessKeyId: config.accessKeyId,
|
|
62
|
+
secretAccessKey: config.secretAccessKey,
|
|
63
|
+
},
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
40
66
|
}
|
|
67
|
+
// -------------------------------------------------------------------------
|
|
68
|
+
// Upload
|
|
69
|
+
// -------------------------------------------------------------------------
|
|
41
70
|
/**
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Generates a file name for S3
|
|
54
|
-
* @param originalName - The original file name
|
|
55
|
-
* @param customName - Optional custom file name
|
|
56
|
-
* @returns Generated file name
|
|
57
|
-
*/
|
|
58
|
-
generateFileName(originalName) {
|
|
59
|
-
const timestamp = Date.now();
|
|
60
|
-
const extension = originalName.includes(".")
|
|
61
|
-
? originalName.substring(originalName.lastIndexOf("."))
|
|
62
|
-
: "";
|
|
63
|
-
const baseName = originalName.includes(".")
|
|
64
|
-
? originalName.substring(0, originalName.lastIndexOf("."))
|
|
65
|
-
: originalName;
|
|
66
|
-
return `${baseName}-${timestamp}${extension}`;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Uploads a single file to S3
|
|
70
|
-
* @param file - The file to upload (Buffer or Multer file)
|
|
71
|
-
* @param options - Upload options
|
|
72
|
-
* @returns Upload result
|
|
71
|
+
* Uploads a file to S3 and returns storage metadata (no URL).
|
|
72
|
+
*
|
|
73
|
+
* The returned `key` is what you persist to your database. Generate signed
|
|
74
|
+
* URLs on demand via `getSignedUrl()` — never store them, as they expire.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* // In a pipeline step:
|
|
78
|
+
* const result = await fileManager.upload(ctx.fileInput, { key: ctx.s3Key });
|
|
79
|
+
* ctx.uploadResult = result;
|
|
73
80
|
*/
|
|
74
81
|
async upload(file, options = {}) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
const buffer = Buffer.from(await file.arrayBuffer());
|
|
79
|
-
if (!buffer) {
|
|
80
|
-
throw new Error("Invalid file object");
|
|
81
|
-
}
|
|
82
|
-
const fileName = options.generateFileName
|
|
83
|
-
? this.generateFileName(file.name)
|
|
84
|
-
: options.fileName || file.name;
|
|
85
|
-
const key = this.getFullKey(fileName, options.folder);
|
|
86
|
-
const uploadParams = {
|
|
82
|
+
const { storedName, key } = this.resolveKey(file.originalName, options);
|
|
83
|
+
const command = new PutObjectCommand({
|
|
87
84
|
Bucket: this.bucketName,
|
|
88
85
|
Key: key,
|
|
89
|
-
Body: buffer,
|
|
90
|
-
ContentType: options.contentType
|
|
91
|
-
ACL: options.acl || this.acl,
|
|
86
|
+
Body: file.buffer,
|
|
87
|
+
ContentType: options.contentType ?? file.mimeType,
|
|
92
88
|
Metadata: options.metadata,
|
|
93
|
-
StorageClass: options.storageClass
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if (uploadParams.ACL === "public-read") {
|
|
100
|
-
// Public URL
|
|
101
|
-
url = `https://${this.bucketName}.s3.amazonaws.com/${key}`;
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
// Signed URL
|
|
105
|
-
const getCommand = new GetObjectCommand({
|
|
106
|
-
Bucket: this.bucketName,
|
|
107
|
-
Key: key,
|
|
108
|
-
});
|
|
109
|
-
url = await getSignedUrl(this.s3Client, getCommand, {
|
|
110
|
-
expiresIn: this.urlExpirationSeconds,
|
|
111
|
-
});
|
|
112
|
-
}
|
|
89
|
+
StorageClass: options.storageClass ?? this.storageClass,
|
|
90
|
+
// ACL intentionally omitted — bucket should have BucketOwnerEnforced
|
|
91
|
+
// (AWS default since April 2023). Pass options.acl explicitly only if
|
|
92
|
+
// your bucket was created before that change and requires legacy ACLs.
|
|
93
|
+
});
|
|
94
|
+
const response = await this.s3.send(command);
|
|
113
95
|
return {
|
|
114
96
|
key,
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
originalName: file.name,
|
|
97
|
+
originalName: file.originalName,
|
|
98
|
+
storedName,
|
|
118
99
|
size: file.size,
|
|
119
|
-
contentType: file.
|
|
120
|
-
etag:
|
|
121
|
-
storageClass:
|
|
100
|
+
contentType: options.contentType ?? file.mimeType,
|
|
101
|
+
etag: response.ETag?.replace(/"/g, ""),
|
|
102
|
+
storageClass: (options.storageClass ?? this.storageClass),
|
|
122
103
|
};
|
|
123
104
|
}
|
|
105
|
+
// -------------------------------------------------------------------------
|
|
106
|
+
// Signed URL — for serving private files to clients
|
|
107
|
+
// -------------------------------------------------------------------------
|
|
108
|
+
/**
|
|
109
|
+
* Generates a short-lived signed URL for a private S3 object.
|
|
110
|
+
*
|
|
111
|
+
* Call this at request time; never store the returned URL. Signed URLs
|
|
112
|
+
* are credentials — treat them accordingly.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* const url = await fileManager.getSignedUrl(file.s3Key, {
|
|
116
|
+
* disposition: 'inline',
|
|
117
|
+
* fileName: file.originalName,
|
|
118
|
+
* });
|
|
119
|
+
*/
|
|
120
|
+
async getSignedUrl(key, options = {}) {
|
|
121
|
+
const disposition = this.buildContentDisposition(options);
|
|
122
|
+
const command = new GetObjectCommand({
|
|
123
|
+
Bucket: this.bucketName,
|
|
124
|
+
Key: key,
|
|
125
|
+
...(disposition && { ResponseContentDisposition: disposition }),
|
|
126
|
+
});
|
|
127
|
+
return getSignedUrl(this.s3, command, {
|
|
128
|
+
expiresIn: options.expiresIn ?? this.urlExpirationSeconds,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// -------------------------------------------------------------------------
|
|
132
|
+
// Download
|
|
133
|
+
// -------------------------------------------------------------------------
|
|
124
134
|
/**
|
|
125
|
-
* Downloads a
|
|
126
|
-
*
|
|
127
|
-
*
|
|
135
|
+
* Downloads an S3 object as a Buffer or a Node.js Readable stream.
|
|
136
|
+
*
|
|
137
|
+
* Use `'stream'` (default) when piping to a response or writing to disk.
|
|
138
|
+
* Use `'buffer'` when you need the full bytes in memory (e.g. for processing).
|
|
139
|
+
*
|
|
140
|
+
* Returns `null` when the object does not exist.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* const result = await fileManager.download(key, 'buffer');
|
|
144
|
+
* if (result) {
|
|
145
|
+
* const { buffer, metadata } = result;
|
|
146
|
+
* }
|
|
128
147
|
*/
|
|
129
148
|
async download(key, mode) {
|
|
130
149
|
try {
|
|
@@ -132,19 +151,18 @@ class AwsFileManager {
|
|
|
132
151
|
Bucket: this.bucketName,
|
|
133
152
|
Key: key,
|
|
134
153
|
});
|
|
135
|
-
const
|
|
136
|
-
if (!
|
|
154
|
+
const response = await this.s3.send(command);
|
|
155
|
+
if (!response.Body)
|
|
137
156
|
return null;
|
|
138
157
|
const metadata = {
|
|
139
|
-
contentType:
|
|
140
|
-
contentLength:
|
|
141
|
-
lastModified:
|
|
142
|
-
metadata:
|
|
143
|
-
storageClass:
|
|
158
|
+
contentType: response.ContentType,
|
|
159
|
+
contentLength: response.ContentLength,
|
|
160
|
+
lastModified: response.LastModified,
|
|
161
|
+
metadata: response.Metadata,
|
|
162
|
+
storageClass: response.StorageClass,
|
|
144
163
|
};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const stream = result.Body;
|
|
164
|
+
if ((mode ?? "stream") === "buffer") {
|
|
165
|
+
const stream = response.Body;
|
|
148
166
|
const chunks = [];
|
|
149
167
|
const buffer = await new Promise((resolve, reject) => {
|
|
150
168
|
stream.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
|
|
@@ -154,28 +172,245 @@ class AwsFileManager {
|
|
|
154
172
|
return { buffer, metadata };
|
|
155
173
|
}
|
|
156
174
|
return {
|
|
157
|
-
stream:
|
|
175
|
+
stream: response.Body,
|
|
158
176
|
metadata,
|
|
159
177
|
};
|
|
160
178
|
}
|
|
161
179
|
catch (error) {
|
|
162
|
-
|
|
163
|
-
|
|
180
|
+
if (isNoSuchKeyError(error))
|
|
181
|
+
return null;
|
|
182
|
+
throw error;
|
|
164
183
|
}
|
|
165
184
|
}
|
|
185
|
+
// -------------------------------------------------------------------------
|
|
186
|
+
// Delete
|
|
187
|
+
// -------------------------------------------------------------------------
|
|
188
|
+
/**
|
|
189
|
+
* Permanently deletes an object from S3.
|
|
190
|
+
*
|
|
191
|
+
* Note: this only removes the S3 object. Your database record should be
|
|
192
|
+
* soft-deleted first, and this called as a cleanup step after the DB
|
|
193
|
+
* transaction commits — so a crash mid-way leaves a recoverable orphan
|
|
194
|
+
* rather than a missing file with an intact DB row.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* await fileManager.delete(file.s3Key);
|
|
198
|
+
*/
|
|
199
|
+
async delete(key) {
|
|
200
|
+
const command = new DeleteObjectCommand({
|
|
201
|
+
Bucket: this.bucketName,
|
|
202
|
+
Key: key,
|
|
203
|
+
});
|
|
204
|
+
await this.s3.send(command);
|
|
205
|
+
// S3 DeleteObject is idempotent — deleting a non-existent key succeeds silently.
|
|
206
|
+
}
|
|
166
207
|
/**
|
|
167
|
-
*
|
|
168
|
-
*
|
|
208
|
+
* Deletes multiple objects in a single call.
|
|
209
|
+
* Silently skips keys that do not exist.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* await fileManager.deleteMany([original.s3Key, thumbnail.s3Key]);
|
|
213
|
+
*/
|
|
214
|
+
async deleteMany(keys) {
|
|
215
|
+
// S3 batch delete supports up to 1000 keys per request
|
|
216
|
+
const chunks = chunkArray(keys, 1000);
|
|
217
|
+
await Promise.all(chunks.map((chunk) => {
|
|
218
|
+
// Build the delete command inline to avoid importing DeleteObjectsCommand
|
|
219
|
+
// above — keeping the import list tidy and tree-shakeable.
|
|
220
|
+
return Promise.all(chunk.map((key) => this.delete(key)));
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
// -------------------------------------------------------------------------
|
|
224
|
+
// Copy
|
|
225
|
+
// -------------------------------------------------------------------------
|
|
226
|
+
/**
|
|
227
|
+
* Copies an object within the same bucket.
|
|
228
|
+
*
|
|
229
|
+
* Useful when duplicating files across entities (e.g. cloning a note)
|
|
230
|
+
* or reorganising keys without re-uploading bytes.
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* await fileManager.copy(sourceFile.s3Key, newKey);
|
|
234
|
+
*/
|
|
235
|
+
async copy(sourceKey, destinationKey, options = {}) {
|
|
236
|
+
const command = new CopyObjectCommand({
|
|
237
|
+
Bucket: this.bucketName,
|
|
238
|
+
CopySource: `${this.bucketName}/${sourceKey}`,
|
|
239
|
+
Key: destinationKey,
|
|
240
|
+
StorageClass: options.storageClass ?? this.storageClass,
|
|
241
|
+
...(options.metadata && {
|
|
242
|
+
Metadata: options.metadata,
|
|
243
|
+
MetadataDirective: "REPLACE",
|
|
244
|
+
}),
|
|
245
|
+
});
|
|
246
|
+
await this.s3.send(command);
|
|
247
|
+
}
|
|
248
|
+
// -------------------------------------------------------------------------
|
|
249
|
+
// List
|
|
250
|
+
// -------------------------------------------------------------------------
|
|
251
|
+
/**
|
|
252
|
+
* Lists objects under a folder prefix.
|
|
253
|
+
*
|
|
254
|
+
* Results are paginated. Pass the returned `continuationToken` back
|
|
255
|
+
* into subsequent calls to walk through large result sets.
|
|
256
|
+
*
|
|
257
|
+
* Primarily intended for the server-side storage reconciliation job
|
|
258
|
+
* (nightly scan to reconcile DB byte counts against S3 actuals).
|
|
259
|
+
*
|
|
260
|
+
* @example
|
|
261
|
+
* let token: string | undefined;
|
|
262
|
+
* do {
|
|
263
|
+
* const result = await fileManager.list({ folder: 'acme-corp', continuationToken: token });
|
|
264
|
+
* processBatch(result.entries);
|
|
265
|
+
* token = result.continuationToken;
|
|
266
|
+
* } while (result.hasMore);
|
|
267
|
+
*/
|
|
268
|
+
async list(options = {}) {
|
|
269
|
+
const prefix = this.buildPrefix(options.folder, options.prefix);
|
|
270
|
+
const command = new ListObjectsV2Command({
|
|
271
|
+
Bucket: this.bucketName,
|
|
272
|
+
Prefix: prefix || undefined,
|
|
273
|
+
MaxKeys: options.maxResults ?? 1000,
|
|
274
|
+
ContinuationToken: options.continuationToken,
|
|
275
|
+
});
|
|
276
|
+
const response = await this.s3.send(command);
|
|
277
|
+
const entries = (response.Contents ?? []).map((obj) => ({
|
|
278
|
+
key: obj.Key ?? "",
|
|
279
|
+
fileName: (obj.Key ?? "").split("/").pop() ?? "",
|
|
280
|
+
size: obj.Size ?? 0,
|
|
281
|
+
lastModified: obj.LastModified ?? new Date(),
|
|
282
|
+
storageClass: obj.StorageClass,
|
|
283
|
+
etag: obj.ETag?.replace(/"/g, ""),
|
|
284
|
+
}));
|
|
285
|
+
return {
|
|
286
|
+
entries,
|
|
287
|
+
continuationToken: response.NextContinuationToken,
|
|
288
|
+
hasMore: response.IsTruncated ?? false,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
// -------------------------------------------------------------------------
|
|
292
|
+
// Key existence check
|
|
293
|
+
// -------------------------------------------------------------------------
|
|
294
|
+
/**
|
|
295
|
+
* Returns true if an object exists at the given key.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* if (!(await fileManager.exists(key))) {
|
|
299
|
+
* throw new Error('Referenced file is missing from storage');
|
|
300
|
+
* }
|
|
301
|
+
*/
|
|
302
|
+
async exists(key) {
|
|
303
|
+
try {
|
|
304
|
+
const command = new GetObjectCommand({
|
|
305
|
+
Bucket: this.bucketName,
|
|
306
|
+
Key: key,
|
|
307
|
+
});
|
|
308
|
+
await this.s3.send(command);
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
if (isNoSuchKeyError(error))
|
|
313
|
+
return false;
|
|
314
|
+
throw error;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// -------------------------------------------------------------------------
|
|
318
|
+
// Expose underlying client for advanced use cases
|
|
319
|
+
// -------------------------------------------------------------------------
|
|
320
|
+
/**
|
|
321
|
+
* Returns the underlying S3Client for operations not covered by this class.
|
|
322
|
+
* Use sparingly — prefer adding methods here so the abstraction stays coherent.
|
|
169
323
|
*/
|
|
170
324
|
getS3Client() {
|
|
171
|
-
return this.
|
|
325
|
+
return this.s3;
|
|
326
|
+
}
|
|
327
|
+
// -------------------------------------------------------------------------
|
|
328
|
+
// Private helpers
|
|
329
|
+
// -------------------------------------------------------------------------
|
|
330
|
+
/**
|
|
331
|
+
* Resolves the final S3 key and stored filename from upload options.
|
|
332
|
+
*
|
|
333
|
+
* Priority order for the key:
|
|
334
|
+
* 1. options.key (explicit full key — pipeline usage)
|
|
335
|
+
* 2. basePath + folder + fileName (if provided)
|
|
336
|
+
* 3. basePath + folder + UUID-based name (if generateUniqueFileName)
|
|
337
|
+
* 4. basePath + folder + originalName (fallback)
|
|
338
|
+
*/
|
|
339
|
+
resolveKey(originalName, options) {
|
|
340
|
+
if (options.key) {
|
|
341
|
+
const storedName = options.key.split("/").pop() ?? originalName;
|
|
342
|
+
return { key: options.key, storedName };
|
|
343
|
+
}
|
|
344
|
+
const storedName = options.generateUniqueFileName
|
|
345
|
+
? this.makeUniqueFileName(originalName)
|
|
346
|
+
: (options.fileName ?? originalName);
|
|
347
|
+
const key = this.buildFullKey(storedName, options.folder);
|
|
348
|
+
return { key, storedName };
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Builds a full S3 key from basePath, optional folder, and filename.
|
|
352
|
+
*/
|
|
353
|
+
buildFullKey(fileName, folder) {
|
|
354
|
+
const folderPart = folder ? folder.replace(/^\/|\/$/g, "") : "";
|
|
355
|
+
return [this.basePath, folderPart, fileName]
|
|
356
|
+
.filter((part) => part.length > 0)
|
|
357
|
+
.join("/");
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Builds a prefix string for list operations.
|
|
361
|
+
*/
|
|
362
|
+
buildPrefix(folder, prefix) {
|
|
363
|
+
return [
|
|
364
|
+
this.basePath,
|
|
365
|
+
folder ? folder.replace(/^\/|\/$/g, "") : "",
|
|
366
|
+
prefix ?? "",
|
|
367
|
+
]
|
|
368
|
+
.filter((part) => part.length > 0)
|
|
369
|
+
.join("/");
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Generates a UUID-based filename, preserving the original extension.
|
|
373
|
+
* UUIDs are collision-proof under any upload concurrency.
|
|
374
|
+
*/
|
|
375
|
+
makeUniqueFileName(originalName) {
|
|
376
|
+
const ext = originalName.includes(".")
|
|
377
|
+
? originalName.substring(originalName.lastIndexOf("."))
|
|
378
|
+
: "";
|
|
379
|
+
return `${randomUUID()}${ext}`;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Builds a Content-Disposition header value from SignedUrlOptions.
|
|
383
|
+
*/
|
|
384
|
+
buildContentDisposition(options) {
|
|
385
|
+
if (!options.disposition)
|
|
386
|
+
return undefined;
|
|
387
|
+
if (options.disposition === "attachment" && options.fileName) {
|
|
388
|
+
const safe = encodeURIComponent(options.fileName);
|
|
389
|
+
return `attachment; filename="${safe}"; filename*=UTF-8''${safe}`;
|
|
390
|
+
}
|
|
391
|
+
return options.disposition; // 'inline' or bare 'attachment'
|
|
172
392
|
}
|
|
173
393
|
}
|
|
174
394
|
AwsFileManager.DEFAULTS = {
|
|
175
|
-
// Default storage class for all AwsFileManager instances
|
|
176
395
|
storageClass: StorageClass.INTELLIGENT_TIERING,
|
|
177
|
-
// 1 hour
|
|
178
396
|
urlExpirationSeconds: 3600,
|
|
179
397
|
};
|
|
180
|
-
|
|
398
|
+
// ---------------------------------------------------------------------------
|
|
399
|
+
// Module-level utilities
|
|
400
|
+
// ---------------------------------------------------------------------------
|
|
401
|
+
/** Type guard for S3 NoSuchKey errors */
|
|
402
|
+
function isNoSuchKeyError(error) {
|
|
403
|
+
return (typeof error === "object" &&
|
|
404
|
+
error !== null &&
|
|
405
|
+
"name" in error &&
|
|
406
|
+
error.name === "NoSuchKey");
|
|
407
|
+
}
|
|
408
|
+
/** Splits an array into chunks of at most `size` elements */
|
|
409
|
+
function chunkArray(arr, size) {
|
|
410
|
+
const chunks = [];
|
|
411
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
412
|
+
chunks.push(arr.slice(i, i + size));
|
|
413
|
+
}
|
|
414
|
+
return chunks;
|
|
415
|
+
}
|
|
181
416
|
//# sourceMappingURL=aws-file-manager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aws-file-manager.js","sourceRoot":"","sources":["../../lib/aws-file-manager.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"aws-file-manager.js","sourceRoot":"","sources":["../../lib/aws-file-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|