@hdriel/aws-utils 1.1.4 → 1.1.6
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 +82 -283
- package/dist/index.cjs +74 -64
- package/dist/index.d.cts +17 -14
- package/dist/index.d.ts +17 -14
- package/dist/index.js +69 -59
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -114,27 +114,19 @@ please see this project code before using: [aws-utils-demo github link!](https:/
|
|
|
114
114
|
|
|
115
115
|
## Core Features
|
|
116
116
|
|
|
117
|
-
###
|
|
117
|
+
### C.R.U.D Bucket Operations
|
|
118
118
|
|
|
119
|
-
#### Initialize Bucket
|
|
120
119
|
```typescript
|
|
121
|
-
//
|
|
122
|
-
await s3.initBucket('private');
|
|
120
|
+
// CREATE
|
|
121
|
+
await s3.initBucket('private'); // Create private bucket (if not exists)
|
|
122
|
+
await s3.initBucket('public-read'); // Create public bucket (if not exists)
|
|
123
|
+
// Could provided includeConstraintLocation option, like:
|
|
124
|
+
await s3.initBucket('private', { includeConstraintLocation: true} );
|
|
123
125
|
|
|
124
|
-
//
|
|
125
|
-
await s3.
|
|
126
|
-
|
|
127
|
-
// With location constraint
|
|
128
|
-
await s3.initBucket('private', {
|
|
129
|
-
includeConstraintLocation: true
|
|
130
|
-
});
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
#### Bucket Information
|
|
134
|
-
```typescript
|
|
126
|
+
// READ
|
|
127
|
+
const exists = await s3.isBucketExists(); // check for existance bucket
|
|
135
128
|
const info = await s3.bucketInfo();
|
|
136
|
-
|
|
137
|
-
// {
|
|
129
|
+
// info = {
|
|
138
130
|
// name: 'my-bucket',
|
|
139
131
|
// region: 'us-east-1',
|
|
140
132
|
// exists: true,
|
|
@@ -144,44 +136,47 @@ console.log(info);
|
|
|
144
136
|
// publicAccessBlock: { ... },
|
|
145
137
|
// policy: { ... }
|
|
146
138
|
// }
|
|
147
|
-
```
|
|
148
139
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
140
|
+
const buckets = await s3.getBucketList(); // get all bucket list from aws s3 storage
|
|
141
|
+
// Could get bucket list with public access info like:
|
|
142
|
+
/*
|
|
143
|
+
bucket list option: {
|
|
144
|
+
Name?: string | undefined;
|
|
145
|
+
CreationDate?: Date | undefined;
|
|
146
|
+
BucketRegion?: string | undefined;
|
|
147
|
+
BucketArn?: string | undefined;
|
|
148
|
+
PublicAccessBlockConfiguration: {
|
|
149
|
+
BlockPublicAcls?: boolean | undefined;
|
|
150
|
+
IgnorePublicAcls?: boolean | undefined;
|
|
151
|
+
BlockPublicPolicy?: boolean | undefined;
|
|
152
|
+
RestrictPublicBuckets?: boolean | undefined;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
*/
|
|
156
|
+
const bucketsWithAccess = await s3.getBucketList({ includePublicAccess: true });
|
|
153
157
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// Delete bucket (must be empty)
|
|
157
|
-
await s3.destroyBucket();
|
|
158
|
+
// UPDATE
|
|
159
|
+
s3.changeBucket('another-bucket'); // Switch to different bucket
|
|
158
160
|
|
|
159
|
-
//
|
|
160
|
-
await s3.destroyBucket(
|
|
161
|
+
// DELETE
|
|
162
|
+
await s3.destroyBucket(); // delete empty bucket
|
|
163
|
+
await s3.destroyBucket(true); // Force delete with all contents and bucket
|
|
161
164
|
```
|
|
162
165
|
|
|
163
|
-
|
|
164
|
-
```typescript
|
|
165
|
-
const buckets = await s3.getBucketList();
|
|
166
|
+
### 📁 C.R.U.D Directory Operations
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
includePublicAccess: true
|
|
170
|
-
});
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### 📁 Directory Operations
|
|
168
|
+
* auto decodeURIComponent for all directory input params
|
|
169
|
+
* handle directory issue (no matter if prefix/postfix slashes)
|
|
174
170
|
|
|
175
171
|
#### Create Directory
|
|
176
172
|
```typescript
|
|
173
|
+
// CREATE
|
|
177
174
|
await s3.createDirectory('/uploads/images');
|
|
178
|
-
```
|
|
179
175
|
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
// READ
|
|
177
|
+
const exists = await s3.directoryExists('/uploads/images'); // check for existance directory
|
|
182
178
|
const { directories, files } = await s3.directoryList('/uploads');
|
|
183
|
-
|
|
184
|
-
console.log('Subdirectories:', directories);
|
|
179
|
+
console.log('Subdirectories:', directories); // string[] directories like: ['images', 'test']
|
|
185
180
|
console.log('Files:', files);
|
|
186
181
|
// files: [
|
|
187
182
|
// {
|
|
@@ -192,36 +187,24 @@ console.log('Files:', files);
|
|
|
192
187
|
// Location: 'https://...'
|
|
193
188
|
// }
|
|
194
189
|
// ]
|
|
195
|
-
```
|
|
196
190
|
|
|
197
|
-
#### Paginated Directory Listing
|
|
198
|
-
```typescript
|
|
199
191
|
// Get second page with 50 items per page
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
192
|
+
const { directories, files, totalFetched } = await s3.directoryListPaginated('/uploads', {
|
|
193
|
+
pageSize: 50,
|
|
194
|
+
pageNumber: 1 // pageNumber is zero base (0-page one, 1- page two, ...)
|
|
203
195
|
});
|
|
204
196
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
console.log(result.totalFetched); // Number of items returned
|
|
208
|
-
```
|
|
197
|
+
// DELETE
|
|
198
|
+
await s3.deleteDirectory('/uploads/temp'); // Delete directory and all contents
|
|
209
199
|
|
|
210
|
-
#### Delete Directory
|
|
211
|
-
```typescript
|
|
212
|
-
// Delete directory and all contents
|
|
213
|
-
await s3.deleteDirectory('/uploads/temp');
|
|
214
200
|
```
|
|
215
201
|
|
|
216
|
-
#### Check Directory Exists
|
|
217
|
-
```typescript
|
|
218
|
-
const exists = await s3.directoryExists('/uploads/images');
|
|
219
|
-
```
|
|
220
202
|
|
|
221
|
-
### 📄 File Operations
|
|
203
|
+
### 📄 C.R.U.D File Operations
|
|
222
204
|
|
|
223
|
-
#### Upload File
|
|
224
205
|
```typescript
|
|
206
|
+
// CREATE
|
|
207
|
+
// > Upload File
|
|
225
208
|
import { ACLs } from '@hdriel/aws-utils';
|
|
226
209
|
|
|
227
210
|
// Upload buffer
|
|
@@ -232,79 +215,45 @@ await s3.uploadFile('/public/image.jpg', buffer, ACLs.public_read);
|
|
|
232
215
|
|
|
233
216
|
// Upload with version tag
|
|
234
217
|
await s3.uploadFile('/docs/v2.pdf', buffer, ACLs.private, '2.0.0');
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
#### Check File Exists
|
|
238
|
-
```typescript
|
|
239
|
-
const exists = await s3.fileExists('/documents/file.pdf');
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
#### Get File Content
|
|
243
|
-
```typescript
|
|
244
|
-
// As buffer
|
|
245
|
-
const buffer = await s3.fileContent('/documents/file.pdf');
|
|
246
218
|
|
|
247
|
-
//
|
|
248
|
-
const
|
|
219
|
+
// > Generate Presigned URL
|
|
220
|
+
const url = await s3.fileUrl('/private/document.pdf'); // Expires in 15 minutes (default)
|
|
221
|
+
const url = await s3.fileUrl('/private/document.pdf', '1h'); // Custom expiration in string value
|
|
222
|
+
const url = await s3.fileUrl('/private/document.pdf', 3600); // Custom expiration in seconds value
|
|
249
223
|
|
|
250
|
-
//
|
|
251
|
-
const
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
#### File Information
|
|
255
|
-
```typescript
|
|
224
|
+
// READ
|
|
225
|
+
const exists = await s3.fileExists('/documents/file.pdf'); // check for existance file
|
|
256
226
|
const info = await s3.fileInfo('/documents/file.pdf');
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
227
|
+
const files = await s3.fileListInfo('/documents'); // List all files in directory
|
|
228
|
+
const pdfFiles = await s3.fileListInfo('/documents', 'report-'); // List files with prefix
|
|
229
|
+
// Paginated file listing - Recommanded way!
|
|
230
|
+
const { files, totalFetched } = await s3.fileListInfoPaginated('/documents', {
|
|
231
|
+
fileNamePrefix: 'invoice-',
|
|
232
|
+
pageSize: 100,
|
|
233
|
+
pageNumber: 0
|
|
234
|
+
});
|
|
235
|
+
const version = await s3.fileVersion('/documents/file.pdf'); // Get file version
|
|
261
236
|
|
|
262
|
-
#### List Files
|
|
263
|
-
```typescript
|
|
264
|
-
// List all files in directory
|
|
265
|
-
const files = await s3.fileListInfo('/documents');
|
|
266
237
|
|
|
267
|
-
//
|
|
268
|
-
const
|
|
238
|
+
// > Get File Content
|
|
239
|
+
const buffer = await s3.fileContent('/documents/file.pdf'); // As buffer
|
|
240
|
+
const base64 = await s3.fileContent('/image.jpg', 'base64'); // As base64 string
|
|
241
|
+
const text = await s3.fileContent('/data.json', 'utf8'); // As UTF-8 string
|
|
269
242
|
|
|
270
|
-
//
|
|
271
|
-
const { files, totalFetched } = await s3.fileListInfoPaginated('/documents', {
|
|
272
|
-
fileNamePrefix: 'invoice-',
|
|
273
|
-
pageSize: 100,
|
|
274
|
-
pageNumber: 0
|
|
275
|
-
});
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
#### File Size
|
|
279
|
-
```typescript
|
|
243
|
+
// > Get File Size
|
|
280
244
|
const bytes = await s3.sizeOf('/large-file.zip');
|
|
281
245
|
const kb = await s3.sizeOf('/large-file.zip', 'KB');
|
|
282
246
|
const mb = await s3.sizeOf('/large-file.zip', 'MB');
|
|
283
247
|
const gb = await s3.sizeOf('/large-file.zip', 'GB');
|
|
284
|
-
```
|
|
285
248
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
await s3.
|
|
289
|
-
```
|
|
249
|
+
// UPDATE
|
|
250
|
+
// > File Tagging
|
|
251
|
+
await s3.taggingFile('/documents/file.pdf', {Key: 'version', Value: '1.0.0'}); // Tag file with version
|
|
290
252
|
|
|
291
|
-
#### Generate Presigned URL
|
|
292
|
-
```typescript
|
|
293
|
-
// Expires in 15 minutes (default)
|
|
294
|
-
const url = await s3.fileUrl('/private/document.pdf');
|
|
295
|
-
|
|
296
|
-
// Custom expiration
|
|
297
|
-
const url = await s3.fileUrl('/private/document.pdf', '1h');
|
|
298
|
-
const url = await s3.fileUrl('/private/document.pdf', 3600); // seconds
|
|
299
|
-
```
|
|
300
253
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
// Tag file with version
|
|
304
|
-
await s3.taggingFile('/documents/file.pdf', '1.0.0');
|
|
254
|
+
// DELETE
|
|
255
|
+
await s3.deleteFile('/documents/old-file.pdf');
|
|
305
256
|
|
|
306
|
-
// Get file version
|
|
307
|
-
const version = await s3.fileVersion('/documents/file.pdf');
|
|
308
257
|
```
|
|
309
258
|
|
|
310
259
|
### 🎬 Streaming & Express.js Integration
|
|
@@ -494,11 +443,10 @@ import { S3LocalstackUtil } from '@hdriel/aws-utils';
|
|
|
494
443
|
|
|
495
444
|
const s3 = new S3LocalstackUtil({
|
|
496
445
|
bucket: 'test-bucket',
|
|
497
|
-
endpoint: 'http://localhost:4566',
|
|
498
|
-
region: 'us-east-1',
|
|
499
|
-
accessKeyId: 'test',
|
|
500
|
-
secretAccessKey: 'test',
|
|
501
|
-
s3ForcePathStyle: true
|
|
446
|
+
// endpoint: 'http://localhost:4566', // get from .env file
|
|
447
|
+
// region: 'us-east-1', // get from .env file
|
|
448
|
+
// accessKeyId: 'test', // get from .env file
|
|
449
|
+
// secretAccessKey: 'test', // get from .env file
|
|
502
450
|
});
|
|
503
451
|
|
|
504
452
|
// Use same API as S3Util
|
|
@@ -547,19 +495,6 @@ services:
|
|
|
547
495
|
|
|
548
496
|
### Dynamic Bucket Switching
|
|
549
497
|
|
|
550
|
-
```typescript
|
|
551
|
-
const s3 = new S3Util({
|
|
552
|
-
bucket: 'default-bucket',
|
|
553
|
-
// ... other config
|
|
554
|
-
});
|
|
555
|
-
|
|
556
|
-
// Switch to different bucket
|
|
557
|
-
s3.changeBucket('another-bucket');
|
|
558
|
-
|
|
559
|
-
// Operations now use 'another-bucket'
|
|
560
|
-
await s3.fileExists('/file.txt');
|
|
561
|
-
```
|
|
562
|
-
|
|
563
498
|
### Custom Logger Integration
|
|
564
499
|
|
|
565
500
|
```typescript
|
|
@@ -568,9 +503,9 @@ import { Logger } from 'stack-trace-logger';
|
|
|
568
503
|
const logger = new Logger('S3Service');
|
|
569
504
|
|
|
570
505
|
const s3 = new S3Util({
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
506
|
+
bucket: 'my-bucket',
|
|
507
|
+
reqId: 'request-123',
|
|
508
|
+
logger,
|
|
574
509
|
});
|
|
575
510
|
|
|
576
511
|
// All operations will log with your logger
|
|
@@ -589,152 +524,16 @@ The utility includes optimized HTTP/HTTPS agents:
|
|
|
589
524
|
// - socketTimeout: 30000ms
|
|
590
525
|
```
|
|
591
526
|
|
|
592
|
-
### Batch Operations
|
|
593
|
-
|
|
594
|
-
```typescript
|
|
595
|
-
// Upload multiple files in parallel
|
|
596
|
-
const files = [
|
|
597
|
-
{ path: '/docs/file1.pdf', data: buffer1 },
|
|
598
|
-
{ path: '/docs/file2.pdf', data: buffer2 },
|
|
599
|
-
{ path: '/docs/file3.pdf', data: buffer3 }
|
|
600
|
-
];
|
|
601
|
-
|
|
602
|
-
await Promise.all(
|
|
603
|
-
files.map(file => s3.uploadFile(file.path, file.data))
|
|
604
|
-
);
|
|
605
|
-
|
|
606
|
-
// Delete multiple files
|
|
607
|
-
const filesToDelete = ['/old/file1.txt', '/old/file2.txt'];
|
|
608
|
-
await Promise.all(
|
|
609
|
-
filesToDelete.map(path => s3.deleteFile(path))
|
|
610
|
-
);
|
|
611
|
-
```
|
|
612
527
|
|
|
613
528
|
## 📋 Complete Express.js Example
|
|
529
|
+
# FULL DEMO PROJECT EXAMPLE:
|
|
530
|
+
please see this project code before using: [aws-utils-demo github link!](https://github.com/hdriel/aws-utils-demo)
|
|
531
|
+

|
|
614
532
|
|
|
615
|
-
```typescript
|
|
616
|
-
import express from 'express';
|
|
617
|
-
import { S3Util, ACLs } from '@hdriel/aws-utils';
|
|
618
|
-
|
|
619
|
-
const app = express();
|
|
620
|
-
const s3 = new S3Util({
|
|
621
|
-
bucket: process.env.S3_BUCKET!,
|
|
622
|
-
region: process.env.AWS_REGION,
|
|
623
|
-
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
624
|
-
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
625
|
-
});
|
|
626
|
-
|
|
627
|
-
// Initialize bucket on startup
|
|
628
|
-
(async () => {
|
|
629
|
-
await s3.initBucket();
|
|
630
|
-
console.log('S3 bucket initialized');
|
|
631
|
-
})();
|
|
632
|
-
|
|
633
|
-
// Upload endpoint
|
|
634
|
-
app.post('/api/upload',
|
|
635
|
-
s3.uploadSingleFile('file', '/uploads', {
|
|
636
|
-
maxFileSize: '10MB',
|
|
637
|
-
fileType: ['image', 'application'],
|
|
638
|
-
filename: async (req, file) => {
|
|
639
|
-
const timestamp = Date.now();
|
|
640
|
-
const sanitized = file.originalname.replace(/[^a-zA-Z0-9.-]/g, '_');
|
|
641
|
-
return `${timestamp}-${sanitized}`;
|
|
642
|
-
}
|
|
643
|
-
}),
|
|
644
|
-
async (req, res) => {
|
|
645
|
-
const { key, location, size } = req.s3File!;
|
|
646
|
-
|
|
647
|
-
// Generate temporary URL
|
|
648
|
-
const url = await s3.fileUrl(key, '1h');
|
|
649
|
-
|
|
650
|
-
res.json({ key, location, size, temporaryUrl: url });
|
|
651
|
-
}
|
|
652
|
-
);
|
|
653
|
-
|
|
654
|
-
// Download endpoint
|
|
655
|
-
app.get('/api/download/:key(*)',
|
|
656
|
-
async (req, res, next) => {
|
|
657
|
-
const key = decodeURIComponent(req.params.key);
|
|
658
|
-
const ctrl = await s3.getStreamFileCtrl({
|
|
659
|
-
filePath: key,
|
|
660
|
-
forDownloading: true
|
|
661
|
-
});
|
|
662
|
-
ctrl(req, res, next);
|
|
663
|
-
}
|
|
664
|
-
);
|
|
665
|
-
|
|
666
|
-
// List files endpoint
|
|
667
|
-
app.get('/api/files', async (req, res) => {
|
|
668
|
-
const { page = '0', size = '50' } = req.query;
|
|
669
|
-
|
|
670
|
-
const result = await s3.directoryListPaginated('/uploads', {
|
|
671
|
-
pageNumber: parseInt(page as string),
|
|
672
|
-
pageSize: parseInt(size as string)
|
|
673
|
-
});
|
|
674
|
-
|
|
675
|
-
res.json(result);
|
|
676
|
-
});
|
|
677
|
-
|
|
678
|
-
// Delete file endpoint
|
|
679
|
-
app.delete('/api/files/:key(*)', async (req, res) => {
|
|
680
|
-
const key = decodeURIComponent(req.params.key);
|
|
681
|
-
await s3.deleteFile(key);
|
|
682
|
-
res.json({ success: true });
|
|
683
|
-
});
|
|
684
|
-
|
|
685
|
-
// Video streaming endpoint
|
|
686
|
-
app.get('/api/video/:id',
|
|
687
|
-
async (req, res, next) => {
|
|
688
|
-
const videoPath = `/videos/${req.params.id}.mp4`;
|
|
689
|
-
const ctrl = await s3.getStreamVideoFileCtrl({
|
|
690
|
-
fileKey: videoPath,
|
|
691
|
-
contentType: 'video/mp4',
|
|
692
|
-
bufferMB: 5
|
|
693
|
-
});
|
|
694
|
-
ctrl(req, res, next);
|
|
695
|
-
}
|
|
696
|
-
);
|
|
697
|
-
|
|
698
|
-
app.listen(3000, () => {
|
|
699
|
-
console.log('Server running on port 3000');
|
|
700
|
-
});
|
|
701
|
-
```
|
|
702
|
-
|
|
703
|
-
## 🚀 Performance Tips
|
|
704
|
-
|
|
705
|
-
1. **Use Pagination**: For large directories, always use paginated methods
|
|
706
|
-
2. **Stream Large Files**: Use streaming methods instead of loading entire files into memory
|
|
707
|
-
3. **Connection Pooling**: The built-in connection pooling is optimized for concurrent requests
|
|
708
|
-
4. **Batch Operations**: Use `Promise.all()` for parallel operations when possible
|
|
709
|
-
5. **Presigned URLs**: Generate presigned URLs for direct client uploads/downloads when appropriate
|
|
710
|
-
|
|
711
|
-
## 🛡️ Error Handling
|
|
712
|
-
|
|
713
|
-
```typescript
|
|
714
|
-
try {
|
|
715
|
-
await s3.uploadFile('/docs/file.pdf', buffer);
|
|
716
|
-
} catch (error) {
|
|
717
|
-
if (error.name === 'NotFound' || error.$metadata?.httpStatusCode === 404) {
|
|
718
|
-
console.error('File not found');
|
|
719
|
-
} else {
|
|
720
|
-
console.error('Upload failed:', error);
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
```
|
|
724
533
|
|
|
725
534
|
## 📝 TypeScript Support
|
|
726
535
|
|
|
727
|
-
This package is written in TypeScript and includes full type definitions
|
|
728
|
-
|
|
729
|
-
```typescript
|
|
730
|
-
import type {
|
|
731
|
-
ContentFile,
|
|
732
|
-
FileUploadResponse,
|
|
733
|
-
TreeDirectoryItem,
|
|
734
|
-
UploadedS3File,
|
|
735
|
-
S3UploadOptions
|
|
736
|
-
} from '@hdriel/aws-utils';
|
|
737
|
-
```
|
|
536
|
+
This package is written in TypeScript and includes full type definitions
|
|
738
537
|
|
|
739
538
|
## 👤 Author
|
|
740
539
|
|
package/dist/index.cjs
CHANGED
|
@@ -271,7 +271,7 @@ var LambdaUtil = class {
|
|
|
271
271
|
var import_client_s36 = require("@aws-sdk/client-s3");
|
|
272
272
|
|
|
273
273
|
// src/aws/s3/s3-stream.ts
|
|
274
|
-
var
|
|
274
|
+
var import_pathe2 = __toESM(require("pathe"), 1);
|
|
275
275
|
var import_stream = require("stream");
|
|
276
276
|
var import_util = require("util");
|
|
277
277
|
var import_buffer2 = require("buffer");
|
|
@@ -295,6 +295,7 @@ var s3Limiter = (0, import_p_limit.default)(4);
|
|
|
295
295
|
|
|
296
296
|
// src/utils/helpers.ts
|
|
297
297
|
var import_bytes = __toESM(require("bytes"), 1);
|
|
298
|
+
var import_ms = __toESM(require("ms"), 1);
|
|
298
299
|
var parseRangeHeader = (range, contentLength, chunkSize) => {
|
|
299
300
|
if (!range || !range.startsWith("bytes=")) return null;
|
|
300
301
|
const rangeParts = range.replace("bytes=", "").split("-");
|
|
@@ -316,11 +317,16 @@ var getFileSize = (maxFileSize, defaultMaxFileSize) => {
|
|
|
316
317
|
const fileSize = typeof fileSizeUnitValue === "number" ? fileSizeUnitValue : (0, import_bytes.default)(fileSizeUnitValue);
|
|
317
318
|
return fileSize != null ? fileSize : void 0;
|
|
318
319
|
};
|
|
320
|
+
var getTotalSeconds = (msValue) => {
|
|
321
|
+
const value = (0, import_ms.default)(msValue);
|
|
322
|
+
return value / 1e3;
|
|
323
|
+
};
|
|
319
324
|
|
|
320
325
|
// src/aws/s3/s3-file.ts
|
|
321
326
|
var import_buffer = require("buffer");
|
|
322
327
|
var import_node_stream = require("stream");
|
|
323
|
-
var
|
|
328
|
+
var import_ms2 = __toESM(require("ms"), 1);
|
|
329
|
+
var import_pathe = require("pathe");
|
|
324
330
|
var import_lib_storage = require("@aws-sdk/lib-storage");
|
|
325
331
|
var import_s3_request_presigner = require("@aws-sdk/s3-request-presigner");
|
|
326
332
|
var import_client_s34 = require("@aws-sdk/client-s3");
|
|
@@ -954,10 +960,16 @@ var S3File = class extends S3Directory {
|
|
|
954
960
|
const normalizedKey = getNormalizedPath(filePath);
|
|
955
961
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
956
962
|
const command = new import_client_s34.HeadObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
957
|
-
|
|
963
|
+
const result = yield this.execute(command);
|
|
964
|
+
if (!result) return result;
|
|
965
|
+
return __spreadProps(__spreadValues({}, result), {
|
|
966
|
+
Name: (0, import_pathe.basename)(normalizedKey),
|
|
967
|
+
Key: normalizedKey,
|
|
968
|
+
Location: `${this.link}${normalizedKey == null ? void 0 : normalizedKey.replace(/^\//, "")}`
|
|
969
|
+
});
|
|
958
970
|
});
|
|
959
971
|
}
|
|
960
|
-
|
|
972
|
+
fileList(directoryPath, fileNamePrefix) {
|
|
961
973
|
return __async(this, null, function* () {
|
|
962
974
|
var _a2, _b;
|
|
963
975
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
@@ -985,7 +997,7 @@ var S3File = class extends S3Directory {
|
|
|
985
997
|
});
|
|
986
998
|
}
|
|
987
999
|
// todo: checked!
|
|
988
|
-
|
|
1000
|
+
fileListPaginated(_0) {
|
|
989
1001
|
return __async(this, arguments, function* (directoryPath, {
|
|
990
1002
|
fileNamePrefix,
|
|
991
1003
|
pageNumber = 0,
|
|
@@ -1080,7 +1092,7 @@ var S3File = class extends S3Directory {
|
|
|
1080
1092
|
var _a2;
|
|
1081
1093
|
let normalizedKey = getNormalizedPath(filePath);
|
|
1082
1094
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1083
|
-
const expiresInSeconds = typeof expiresIn === "number" ? expiresIn : (0,
|
|
1095
|
+
const expiresInSeconds = typeof expiresIn === "number" ? expiresIn : (0, import_ms2.default)(expiresIn) / 1e3;
|
|
1084
1096
|
const command = new import_client_s34.GetObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
1085
1097
|
const url = yield (0, import_s3_request_presigner.getSignedUrl)(this.s3Client, command, {
|
|
1086
1098
|
expiresIn: expiresInSeconds
|
|
@@ -1216,22 +1228,20 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1216
1228
|
__publicField(this, "getImageFileViewCtrl", ({
|
|
1217
1229
|
fileKey: _fileKey,
|
|
1218
1230
|
queryField = "file",
|
|
1219
|
-
|
|
1231
|
+
paramField = "file",
|
|
1232
|
+
cachingAgeSeconds = "1y"
|
|
1220
1233
|
} = {}) => {
|
|
1221
1234
|
return (req, res, next) => __async(this, null, function* () {
|
|
1222
|
-
var _a2, _b, _c, _d, _e;
|
|
1223
|
-
let fileKey = _fileKey || (((_a2 = req.
|
|
1235
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
1236
|
+
let fileKey = _fileKey || (((_a2 = req.params) == null ? void 0 : _a2[paramField]) ? decodeURIComponent((_b = req.params) == null ? void 0 : _b[paramField]) : void 0) || (((_c = req.query) == null ? void 0 : _c[queryField]) ? decodeURIComponent((_d = req.query) == null ? void 0 : _d[queryField]) : void 0);
|
|
1224
1237
|
if (!fileKey || fileKey === "/") {
|
|
1225
|
-
(
|
|
1226
|
-
|
|
1227
|
-
queryField
|
|
1228
|
-
});
|
|
1229
|
-
next("image file key is required");
|
|
1238
|
+
(_e = this.logger) == null ? void 0 : _e.warn(req.id, "image fileKey is required");
|
|
1239
|
+
next("image fileKey is required");
|
|
1230
1240
|
return;
|
|
1231
1241
|
}
|
|
1232
1242
|
try {
|
|
1233
1243
|
const imageBuffer = yield this.fileContent(fileKey, "buffer");
|
|
1234
|
-
const ext =
|
|
1244
|
+
const ext = import_pathe2.default.extname(fileKey).slice(1).toLowerCase();
|
|
1235
1245
|
const mimeTypeMap = {
|
|
1236
1246
|
jpg: "image/jpeg",
|
|
1237
1247
|
jpeg: "image/jpeg",
|
|
@@ -1243,14 +1253,14 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1243
1253
|
};
|
|
1244
1254
|
const contentType = mimeTypeMap[ext] || "application/octet-stream";
|
|
1245
1255
|
res.setHeader("Content-Type", contentType);
|
|
1246
|
-
if (cachingAge) res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1247
1256
|
res.setHeader("Content-Length", imageBuffer.length);
|
|
1257
|
+
const cachingAge = !cachingAgeSeconds || typeof cachingAgeSeconds === "number" ? cachingAgeSeconds : getTotalSeconds(cachingAgeSeconds);
|
|
1258
|
+
if (cachingAge) res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1248
1259
|
res.status(200).send(imageBuffer);
|
|
1249
1260
|
} catch (error) {
|
|
1250
|
-
(
|
|
1251
|
-
fileKey
|
|
1252
|
-
|
|
1253
|
-
});
|
|
1261
|
+
(_f = this.logger) == null ? void 0 : _f.warn(req.id, "image fileKey not found", __spreadValues({
|
|
1262
|
+
fileKey
|
|
1263
|
+
}, this.localstack && { localstack: this.localstack }));
|
|
1254
1264
|
next(`Failed to retrieve image file: ${error.message}`);
|
|
1255
1265
|
}
|
|
1256
1266
|
});
|
|
@@ -1259,18 +1269,20 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1259
1269
|
__publicField(this, "getPdfFileViewCtrl", ({
|
|
1260
1270
|
fileKey: _fileKey,
|
|
1261
1271
|
queryField = "file",
|
|
1262
|
-
|
|
1272
|
+
paramField = "file",
|
|
1273
|
+
cachingAgeSeconds = "1y"
|
|
1263
1274
|
} = {}) => {
|
|
1264
1275
|
return (req, res, next) => __async(this, null, function* () {
|
|
1265
|
-
var _a2, _b;
|
|
1266
|
-
let fileKey = _fileKey || (((_a2 = req.
|
|
1276
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
1277
|
+
let fileKey = _fileKey || (((_a2 = req.params) == null ? void 0 : _a2[paramField]) ? decodeURIComponent((_b = req.params) == null ? void 0 : _b[paramField]) : void 0) || (((_c = req.query) == null ? void 0 : _c[queryField]) ? decodeURIComponent((_d = req.query) == null ? void 0 : _d[queryField]) : void 0);
|
|
1267
1278
|
if (!fileKey) {
|
|
1268
|
-
|
|
1279
|
+
(_e = this.logger) == null ? void 0 : _e.warn(req.id, "pdf fileKey is required");
|
|
1280
|
+
next("pdf fileKey is required");
|
|
1269
1281
|
return;
|
|
1270
1282
|
}
|
|
1271
1283
|
try {
|
|
1272
1284
|
const fileBuffer = yield this.fileContent(fileKey, "buffer");
|
|
1273
|
-
const ext =
|
|
1285
|
+
const ext = import_pathe2.default.extname(fileKey).slice(1).toLowerCase();
|
|
1274
1286
|
const mimeTypeMap = {
|
|
1275
1287
|
pdf: "application/pdf",
|
|
1276
1288
|
txt: "text/plain",
|
|
@@ -1283,11 +1295,15 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1283
1295
|
};
|
|
1284
1296
|
const contentType = mimeTypeMap[ext] || "application/octet-stream";
|
|
1285
1297
|
res.setHeader("Content-Type", contentType);
|
|
1286
|
-
res.setHeader("Content-Disposition", `inline; filename="${
|
|
1287
|
-
res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1298
|
+
res.setHeader("Content-Disposition", `inline; filename="${import_pathe2.default.basename(fileKey)}"`);
|
|
1288
1299
|
res.setHeader("Content-Length", fileBuffer.length);
|
|
1300
|
+
const cachingAge = !cachingAgeSeconds || typeof cachingAgeSeconds === "number" ? cachingAgeSeconds : getTotalSeconds(cachingAgeSeconds);
|
|
1301
|
+
res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1289
1302
|
res.status(200).send(fileBuffer);
|
|
1290
1303
|
} catch (error) {
|
|
1304
|
+
(_f = this.logger) == null ? void 0 : _f.warn(req.id, "pdf fileKey not found", __spreadValues({
|
|
1305
|
+
fileKey
|
|
1306
|
+
}, this.localstack && { localstack: this.localstack }));
|
|
1291
1307
|
next(`Failed to retrieve pdf file: ${error.message}`);
|
|
1292
1308
|
}
|
|
1293
1309
|
});
|
|
@@ -1295,12 +1311,12 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1295
1311
|
this.maxUploadFileSizeRestriction = maxUploadFileSizeRestriction;
|
|
1296
1312
|
}
|
|
1297
1313
|
streamObjectFile(_0) {
|
|
1298
|
-
return __async(this, arguments, function* (
|
|
1314
|
+
return __async(this, arguments, function* (fileKey, {
|
|
1299
1315
|
Range,
|
|
1300
1316
|
checkFileExists = true,
|
|
1301
1317
|
abortSignal
|
|
1302
1318
|
} = {}) {
|
|
1303
|
-
let normalizedKey = getNormalizedPath(
|
|
1319
|
+
let normalizedKey = getNormalizedPath(fileKey);
|
|
1304
1320
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1305
1321
|
if (checkFileExists) {
|
|
1306
1322
|
const isExists = yield this.fileExists(normalizedKey);
|
|
@@ -1319,13 +1335,12 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1319
1335
|
}
|
|
1320
1336
|
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1321
1337
|
streamVideoFile(_0) {
|
|
1322
|
-
return __async(this, arguments, function* ({
|
|
1323
|
-
filePath,
|
|
1338
|
+
return __async(this, arguments, function* (fileKey, {
|
|
1324
1339
|
Range,
|
|
1325
1340
|
abortSignal
|
|
1326
|
-
}) {
|
|
1341
|
+
} = {}) {
|
|
1327
1342
|
var _a2;
|
|
1328
|
-
let normalizedKey = getNormalizedPath(
|
|
1343
|
+
let normalizedKey = getNormalizedPath(fileKey);
|
|
1329
1344
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1330
1345
|
try {
|
|
1331
1346
|
const cmd = new import_client_s35.GetObjectCommand(__spreadValues({
|
|
@@ -1349,7 +1364,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1349
1364
|
} catch (error) {
|
|
1350
1365
|
(_a2 = this.logger) == null ? void 0 : _a2.warn(this.reqId, "getS3VideoStream error", {
|
|
1351
1366
|
Bucket: this.bucket,
|
|
1352
|
-
|
|
1367
|
+
fileKey: normalizedKey,
|
|
1353
1368
|
Range,
|
|
1354
1369
|
error
|
|
1355
1370
|
});
|
|
@@ -1407,8 +1422,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1407
1422
|
const onClose = () => abort.abort();
|
|
1408
1423
|
req.once("close", onClose);
|
|
1409
1424
|
try {
|
|
1410
|
-
const result = yield this.streamVideoFile({
|
|
1411
|
-
filePath: normalizedKey,
|
|
1425
|
+
const result = yield this.streamVideoFile(normalizedKey, {
|
|
1412
1426
|
Range,
|
|
1413
1427
|
abortSignal: abort.signal
|
|
1414
1428
|
});
|
|
@@ -1473,11 +1487,10 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1473
1487
|
}
|
|
1474
1488
|
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1475
1489
|
getStreamFileCtrl(_0) {
|
|
1476
|
-
return __async(this, arguments, function* ({
|
|
1477
|
-
filePath,
|
|
1490
|
+
return __async(this, arguments, function* (fileKey, {
|
|
1478
1491
|
filename,
|
|
1479
1492
|
forDownloading = false
|
|
1480
|
-
}) {
|
|
1493
|
+
} = {}) {
|
|
1481
1494
|
return (req, res, next) => __async(this, null, function* () {
|
|
1482
1495
|
var _a2, _b;
|
|
1483
1496
|
const abort = new AbortController();
|
|
@@ -1488,7 +1501,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1488
1501
|
(_a3 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _a3.call(stream);
|
|
1489
1502
|
};
|
|
1490
1503
|
req.once("close", onClose);
|
|
1491
|
-
let normalizedKey = getNormalizedPath(
|
|
1504
|
+
let normalizedKey = getNormalizedPath(fileKey);
|
|
1492
1505
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1493
1506
|
try {
|
|
1494
1507
|
const isExists = yield this.fileExists(normalizedKey);
|
|
@@ -1517,7 +1530,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1517
1530
|
}
|
|
1518
1531
|
stream.on("error", (err) => {
|
|
1519
1532
|
var _a3, _b2;
|
|
1520
|
-
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "Stream error", {
|
|
1533
|
+
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "Stream error", { fileKey: normalizedKey, error: err });
|
|
1521
1534
|
abort.abort();
|
|
1522
1535
|
(_b2 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _b2.call(stream);
|
|
1523
1536
|
});
|
|
@@ -1537,7 +1550,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1537
1550
|
if (isBenignStreamError) {
|
|
1538
1551
|
return;
|
|
1539
1552
|
}
|
|
1540
|
-
(_b = this.logger) == null ? void 0 : _b.error(this.reqId, "Failed to stream file", {
|
|
1553
|
+
(_b = this.logger) == null ? void 0 : _b.error(this.reqId, "Failed to stream file", { fileKey: normalizedKey, error });
|
|
1541
1554
|
if (!res.headersSent) {
|
|
1542
1555
|
next(error);
|
|
1543
1556
|
} else if (!res.writableEnded) {
|
|
@@ -1554,33 +1567,30 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1554
1567
|
}
|
|
1555
1568
|
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1556
1569
|
getStreamZipFileCtr(_0) {
|
|
1557
|
-
return __async(this, arguments, function* ({
|
|
1558
|
-
filePath,
|
|
1570
|
+
return __async(this, arguments, function* (fileKey, {
|
|
1559
1571
|
filename: _filename,
|
|
1560
1572
|
compressionLevel = 5
|
|
1561
|
-
}) {
|
|
1573
|
+
} = {}) {
|
|
1562
1574
|
return (req, res, next) => __async(this, null, function* () {
|
|
1563
1575
|
var _a2, _b, _c, _d, _e;
|
|
1564
|
-
const filePaths = [].concat(filePath).map((filePath2) => getNormalizedPath(filePath2)).filter((v) => v && v !== "/");
|
|
1565
|
-
if (!filePaths.length) {
|
|
1566
|
-
throw new Error("No file keys provided");
|
|
1567
|
-
}
|
|
1568
|
-
let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
|
|
1569
|
-
filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
|
|
1570
1576
|
const abort = new AbortController();
|
|
1571
|
-
const onClose = () =>
|
|
1572
|
-
abort.abort();
|
|
1573
|
-
};
|
|
1574
|
-
req.once("close", onClose);
|
|
1577
|
+
const onClose = () => abort.abort();
|
|
1575
1578
|
try {
|
|
1576
|
-
|
|
1577
|
-
|
|
1579
|
+
const fileKeys = [].concat(fileKey).map((fileKey2) => getNormalizedPath(fileKey2)).filter((v) => v && v !== "/");
|
|
1580
|
+
if (!fileKeys.length) {
|
|
1581
|
+
throw new Error("No file keys provided");
|
|
1582
|
+
}
|
|
1583
|
+
let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
|
|
1584
|
+
filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
|
|
1585
|
+
req.once("close", onClose);
|
|
1586
|
+
(_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, "Starting parallel file download...", { fileCount: fileKeys.length });
|
|
1587
|
+
const downloadPromises = fileKeys.map((fileKey2) => __async(this, null, function* () {
|
|
1578
1588
|
var _a3, _b2, _c2;
|
|
1579
1589
|
try {
|
|
1580
1590
|
if (abort.signal.aborted) return null;
|
|
1581
|
-
const stream = yield this.streamObjectFile(
|
|
1591
|
+
const stream = yield this.streamObjectFile(fileKey2, { abortSignal: abort.signal });
|
|
1582
1592
|
if (!stream) {
|
|
1583
|
-
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", {
|
|
1593
|
+
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { fileKey: fileKey2 });
|
|
1584
1594
|
return null;
|
|
1585
1595
|
}
|
|
1586
1596
|
const chunks = [];
|
|
@@ -1604,14 +1614,14 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1604
1614
|
}
|
|
1605
1615
|
}
|
|
1606
1616
|
const buffer = import_buffer2.Buffer.concat(chunks);
|
|
1607
|
-
const fileName =
|
|
1617
|
+
const fileName = fileKey2.split("/").pop() || fileKey2;
|
|
1608
1618
|
(_b2 = this.logger) == null ? void 0 : _b2.debug(this.reqId, "File downloaded", {
|
|
1609
|
-
|
|
1619
|
+
fileKey: fileKey2,
|
|
1610
1620
|
sizeMB: (buffer.length / (1024 * 1024)).toFixed(2)
|
|
1611
1621
|
});
|
|
1612
|
-
return { buffer, name: fileName, path:
|
|
1622
|
+
return { buffer, name: fileName, path: fileKey2 };
|
|
1613
1623
|
} catch (error2) {
|
|
1614
|
-
(_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", {
|
|
1624
|
+
(_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", { fileKey: fileKey2, error: error2 });
|
|
1615
1625
|
return null;
|
|
1616
1626
|
}
|
|
1617
1627
|
}));
|
|
@@ -1693,7 +1703,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1693
1703
|
static fileFilter(types, fileExt) {
|
|
1694
1704
|
const fileTypesChecker = (fileExt == null ? void 0 : fileExt.length) ? new RegExp(`\\.(${fileExt.join("|")})$`, "i") : void 0;
|
|
1695
1705
|
return function(_req, file, cb) {
|
|
1696
|
-
const fileExtension =
|
|
1706
|
+
const fileExtension = import_pathe2.default.extname(file.originalname).substring(1);
|
|
1697
1707
|
const extname = fileTypesChecker ? fileTypesChecker.test(`.${fileExtension}`) : true;
|
|
1698
1708
|
const mimeType = (types == null ? void 0 : types.length) ? types.some((type) => file.mimetype.startsWith(`${type}/`)) : true;
|
|
1699
1709
|
if (mimeType && extname) {
|
package/dist/index.d.cts
CHANGED
|
@@ -242,11 +242,15 @@ declare class S3Directory extends S3Bucket {
|
|
|
242
242
|
type S3FileProps = S3DirectoryProps;
|
|
243
243
|
declare class S3File extends S3Directory {
|
|
244
244
|
constructor(props: S3FileProps);
|
|
245
|
-
fileInfo(filePath: string): Promise<HeadObjectCommandOutput
|
|
246
|
-
|
|
245
|
+
fileInfo(filePath: string): Promise<HeadObjectCommandOutput & {
|
|
246
|
+
Name: string;
|
|
247
|
+
Location: string;
|
|
248
|
+
Key: string;
|
|
249
|
+
}>;
|
|
250
|
+
fileList(directoryPath?: string, fileNamePrefix?: string): Promise<(ContentFile & {
|
|
247
251
|
Location: string;
|
|
248
252
|
})[]>;
|
|
249
|
-
|
|
253
|
+
fileListPaginated(directoryPath?: string, { fileNamePrefix, pageNumber, // 0-based: page 0 = items 0-99, page 1 = items 100-199, page 2 = items 200-299
|
|
250
254
|
pageSize, }?: {
|
|
251
255
|
fileNamePrefix?: string;
|
|
252
256
|
pageSize?: number;
|
|
@@ -273,13 +277,12 @@ type S3StreamProps = S3FileProps & {
|
|
|
273
277
|
declare class S3Stream extends S3File {
|
|
274
278
|
private readonly maxUploadFileSizeRestriction;
|
|
275
279
|
constructor({ maxUploadFileSizeRestriction, ...props }: S3StreamProps);
|
|
276
|
-
protected streamObjectFile(
|
|
280
|
+
protected streamObjectFile(fileKey: string, { Range, checkFileExists, abortSignal, }?: {
|
|
277
281
|
Range?: string;
|
|
278
282
|
checkFileExists?: boolean;
|
|
279
283
|
abortSignal?: AbortSignal;
|
|
280
284
|
}): Promise<Readable | null>;
|
|
281
|
-
protected streamVideoFile(
|
|
282
|
-
filePath: string;
|
|
285
|
+
protected streamVideoFile(fileKey: string, { Range, abortSignal, }?: {
|
|
283
286
|
Range?: string;
|
|
284
287
|
abortSignal?: AbortSignal;
|
|
285
288
|
}): Promise<{
|
|
@@ -300,23 +303,23 @@ declare class S3Stream extends S3File {
|
|
|
300
303
|
bufferMB?: number | undefined;
|
|
301
304
|
streamTimeoutMS?: number | undefined;
|
|
302
305
|
}): Promise<(req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<any>>;
|
|
303
|
-
getImageFileViewCtrl: ({ fileKey: _fileKey, queryField,
|
|
306
|
+
getImageFileViewCtrl: ({ fileKey: _fileKey, queryField, paramField, cachingAgeSeconds, }?: {
|
|
304
307
|
fileKey?: string;
|
|
305
308
|
queryField?: string;
|
|
306
|
-
|
|
309
|
+
paramField?: string;
|
|
310
|
+
cachingAgeSeconds?: null | number | StringValue;
|
|
307
311
|
}) => (req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<void>;
|
|
308
|
-
getPdfFileViewCtrl: ({ fileKey: _fileKey, queryField,
|
|
312
|
+
getPdfFileViewCtrl: ({ fileKey: _fileKey, queryField, paramField, cachingAgeSeconds, }?: {
|
|
309
313
|
fileKey?: string;
|
|
310
314
|
queryField?: string;
|
|
311
|
-
|
|
315
|
+
paramField?: string;
|
|
316
|
+
cachingAgeSeconds?: null | number | StringValue;
|
|
312
317
|
}) => (req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<void>;
|
|
313
|
-
getStreamFileCtrl(
|
|
314
|
-
filePath: string;
|
|
318
|
+
getStreamFileCtrl(fileKey: string, { filename, forDownloading, }?: {
|
|
315
319
|
filename?: string;
|
|
316
320
|
forDownloading?: boolean;
|
|
317
321
|
}): Promise<(req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<void>>;
|
|
318
|
-
getStreamZipFileCtr(
|
|
319
|
-
filePath: string | string[];
|
|
322
|
+
getStreamZipFileCtr(fileKey: string | string[], { filename: _filename, compressionLevel, }?: {
|
|
320
323
|
filename?: string;
|
|
321
324
|
compressionLevel?: number;
|
|
322
325
|
}): Promise<(req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<void>>;
|
package/dist/index.d.ts
CHANGED
|
@@ -242,11 +242,15 @@ declare class S3Directory extends S3Bucket {
|
|
|
242
242
|
type S3FileProps = S3DirectoryProps;
|
|
243
243
|
declare class S3File extends S3Directory {
|
|
244
244
|
constructor(props: S3FileProps);
|
|
245
|
-
fileInfo(filePath: string): Promise<HeadObjectCommandOutput
|
|
246
|
-
|
|
245
|
+
fileInfo(filePath: string): Promise<HeadObjectCommandOutput & {
|
|
246
|
+
Name: string;
|
|
247
|
+
Location: string;
|
|
248
|
+
Key: string;
|
|
249
|
+
}>;
|
|
250
|
+
fileList(directoryPath?: string, fileNamePrefix?: string): Promise<(ContentFile & {
|
|
247
251
|
Location: string;
|
|
248
252
|
})[]>;
|
|
249
|
-
|
|
253
|
+
fileListPaginated(directoryPath?: string, { fileNamePrefix, pageNumber, // 0-based: page 0 = items 0-99, page 1 = items 100-199, page 2 = items 200-299
|
|
250
254
|
pageSize, }?: {
|
|
251
255
|
fileNamePrefix?: string;
|
|
252
256
|
pageSize?: number;
|
|
@@ -273,13 +277,12 @@ type S3StreamProps = S3FileProps & {
|
|
|
273
277
|
declare class S3Stream extends S3File {
|
|
274
278
|
private readonly maxUploadFileSizeRestriction;
|
|
275
279
|
constructor({ maxUploadFileSizeRestriction, ...props }: S3StreamProps);
|
|
276
|
-
protected streamObjectFile(
|
|
280
|
+
protected streamObjectFile(fileKey: string, { Range, checkFileExists, abortSignal, }?: {
|
|
277
281
|
Range?: string;
|
|
278
282
|
checkFileExists?: boolean;
|
|
279
283
|
abortSignal?: AbortSignal;
|
|
280
284
|
}): Promise<Readable | null>;
|
|
281
|
-
protected streamVideoFile(
|
|
282
|
-
filePath: string;
|
|
285
|
+
protected streamVideoFile(fileKey: string, { Range, abortSignal, }?: {
|
|
283
286
|
Range?: string;
|
|
284
287
|
abortSignal?: AbortSignal;
|
|
285
288
|
}): Promise<{
|
|
@@ -300,23 +303,23 @@ declare class S3Stream extends S3File {
|
|
|
300
303
|
bufferMB?: number | undefined;
|
|
301
304
|
streamTimeoutMS?: number | undefined;
|
|
302
305
|
}): Promise<(req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<any>>;
|
|
303
|
-
getImageFileViewCtrl: ({ fileKey: _fileKey, queryField,
|
|
306
|
+
getImageFileViewCtrl: ({ fileKey: _fileKey, queryField, paramField, cachingAgeSeconds, }?: {
|
|
304
307
|
fileKey?: string;
|
|
305
308
|
queryField?: string;
|
|
306
|
-
|
|
309
|
+
paramField?: string;
|
|
310
|
+
cachingAgeSeconds?: null | number | StringValue;
|
|
307
311
|
}) => (req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<void>;
|
|
308
|
-
getPdfFileViewCtrl: ({ fileKey: _fileKey, queryField,
|
|
312
|
+
getPdfFileViewCtrl: ({ fileKey: _fileKey, queryField, paramField, cachingAgeSeconds, }?: {
|
|
309
313
|
fileKey?: string;
|
|
310
314
|
queryField?: string;
|
|
311
|
-
|
|
315
|
+
paramField?: string;
|
|
316
|
+
cachingAgeSeconds?: null | number | StringValue;
|
|
312
317
|
}) => (req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<void>;
|
|
313
|
-
getStreamFileCtrl(
|
|
314
|
-
filePath: string;
|
|
318
|
+
getStreamFileCtrl(fileKey: string, { filename, forDownloading, }?: {
|
|
315
319
|
filename?: string;
|
|
316
320
|
forDownloading?: boolean;
|
|
317
321
|
}): Promise<(req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<void>>;
|
|
318
|
-
getStreamZipFileCtr(
|
|
319
|
-
filePath: string | string[];
|
|
322
|
+
getStreamZipFileCtr(fileKey: string | string[], { filename: _filename, compressionLevel, }?: {
|
|
320
323
|
filename?: string;
|
|
321
324
|
compressionLevel?: number;
|
|
322
325
|
}): Promise<(req: Request$1 & any, res: Response & any, next: NextFunction & any) => Promise<void>>;
|
package/dist/index.js
CHANGED
|
@@ -256,6 +256,7 @@ var s3Limiter = pLimit(4);
|
|
|
256
256
|
|
|
257
257
|
// src/utils/helpers.ts
|
|
258
258
|
import bytes from "bytes";
|
|
259
|
+
import ms from "ms";
|
|
259
260
|
var parseRangeHeader = (range, contentLength, chunkSize) => {
|
|
260
261
|
if (!range || !range.startsWith("bytes=")) return null;
|
|
261
262
|
const rangeParts = range.replace("bytes=", "").split("-");
|
|
@@ -277,11 +278,16 @@ var getFileSize = (maxFileSize, defaultMaxFileSize) => {
|
|
|
277
278
|
const fileSize = typeof fileSizeUnitValue === "number" ? fileSizeUnitValue : bytes(fileSizeUnitValue);
|
|
278
279
|
return fileSize != null ? fileSize : void 0;
|
|
279
280
|
};
|
|
281
|
+
var getTotalSeconds = (msValue) => {
|
|
282
|
+
const value = ms(msValue);
|
|
283
|
+
return value / 1e3;
|
|
284
|
+
};
|
|
280
285
|
|
|
281
286
|
// src/aws/s3/s3-file.ts
|
|
282
287
|
import { Buffer as Buffer2 } from "buffer";
|
|
283
288
|
import "stream";
|
|
284
|
-
import
|
|
289
|
+
import ms2 from "ms";
|
|
290
|
+
import { basename } from "pathe";
|
|
285
291
|
import { Upload } from "@aws-sdk/lib-storage";
|
|
286
292
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
287
293
|
import {
|
|
@@ -943,10 +949,16 @@ var S3File = class extends S3Directory {
|
|
|
943
949
|
const normalizedKey = getNormalizedPath(filePath);
|
|
944
950
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
945
951
|
const command = new HeadObjectCommand2({ Bucket: this.bucket, Key: normalizedKey });
|
|
946
|
-
|
|
952
|
+
const result = yield this.execute(command);
|
|
953
|
+
if (!result) return result;
|
|
954
|
+
return __spreadProps(__spreadValues({}, result), {
|
|
955
|
+
Name: basename(normalizedKey),
|
|
956
|
+
Key: normalizedKey,
|
|
957
|
+
Location: `${this.link}${normalizedKey == null ? void 0 : normalizedKey.replace(/^\//, "")}`
|
|
958
|
+
});
|
|
947
959
|
});
|
|
948
960
|
}
|
|
949
|
-
|
|
961
|
+
fileList(directoryPath, fileNamePrefix) {
|
|
950
962
|
return __async(this, null, function* () {
|
|
951
963
|
var _a2, _b;
|
|
952
964
|
let normalizedPath = getNormalizedPath(directoryPath);
|
|
@@ -974,7 +986,7 @@ var S3File = class extends S3Directory {
|
|
|
974
986
|
});
|
|
975
987
|
}
|
|
976
988
|
// todo: checked!
|
|
977
|
-
|
|
989
|
+
fileListPaginated(_0) {
|
|
978
990
|
return __async(this, arguments, function* (directoryPath, {
|
|
979
991
|
fileNamePrefix,
|
|
980
992
|
pageNumber = 0,
|
|
@@ -1069,7 +1081,7 @@ var S3File = class extends S3Directory {
|
|
|
1069
1081
|
var _a2;
|
|
1070
1082
|
let normalizedKey = getNormalizedPath(filePath);
|
|
1071
1083
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1072
|
-
const expiresInSeconds = typeof expiresIn === "number" ? expiresIn :
|
|
1084
|
+
const expiresInSeconds = typeof expiresIn === "number" ? expiresIn : ms2(expiresIn) / 1e3;
|
|
1073
1085
|
const command = new GetObjectCommand({ Bucket: this.bucket, Key: normalizedKey });
|
|
1074
1086
|
const url = yield getSignedUrl(this.s3Client, command, {
|
|
1075
1087
|
expiresIn: expiresInSeconds
|
|
@@ -1205,17 +1217,15 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1205
1217
|
__publicField(this, "getImageFileViewCtrl", ({
|
|
1206
1218
|
fileKey: _fileKey,
|
|
1207
1219
|
queryField = "file",
|
|
1208
|
-
|
|
1220
|
+
paramField = "file",
|
|
1221
|
+
cachingAgeSeconds = "1y"
|
|
1209
1222
|
} = {}) => {
|
|
1210
1223
|
return (req, res, next) => __async(this, null, function* () {
|
|
1211
|
-
var _a2, _b, _c, _d, _e;
|
|
1212
|
-
let fileKey = _fileKey || (((_a2 = req.
|
|
1224
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
1225
|
+
let fileKey = _fileKey || (((_a2 = req.params) == null ? void 0 : _a2[paramField]) ? decodeURIComponent((_b = req.params) == null ? void 0 : _b[paramField]) : void 0) || (((_c = req.query) == null ? void 0 : _c[queryField]) ? decodeURIComponent((_d = req.query) == null ? void 0 : _d[queryField]) : void 0);
|
|
1213
1226
|
if (!fileKey || fileKey === "/") {
|
|
1214
|
-
(
|
|
1215
|
-
|
|
1216
|
-
queryField
|
|
1217
|
-
});
|
|
1218
|
-
next("image file key is required");
|
|
1227
|
+
(_e = this.logger) == null ? void 0 : _e.warn(req.id, "image fileKey is required");
|
|
1228
|
+
next("image fileKey is required");
|
|
1219
1229
|
return;
|
|
1220
1230
|
}
|
|
1221
1231
|
try {
|
|
@@ -1232,14 +1242,14 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1232
1242
|
};
|
|
1233
1243
|
const contentType = mimeTypeMap[ext] || "application/octet-stream";
|
|
1234
1244
|
res.setHeader("Content-Type", contentType);
|
|
1235
|
-
if (cachingAge) res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1236
1245
|
res.setHeader("Content-Length", imageBuffer.length);
|
|
1246
|
+
const cachingAge = !cachingAgeSeconds || typeof cachingAgeSeconds === "number" ? cachingAgeSeconds : getTotalSeconds(cachingAgeSeconds);
|
|
1247
|
+
if (cachingAge) res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1237
1248
|
res.status(200).send(imageBuffer);
|
|
1238
1249
|
} catch (error) {
|
|
1239
|
-
(
|
|
1240
|
-
fileKey
|
|
1241
|
-
|
|
1242
|
-
});
|
|
1250
|
+
(_f = this.logger) == null ? void 0 : _f.warn(req.id, "image fileKey not found", __spreadValues({
|
|
1251
|
+
fileKey
|
|
1252
|
+
}, this.localstack && { localstack: this.localstack }));
|
|
1243
1253
|
next(`Failed to retrieve image file: ${error.message}`);
|
|
1244
1254
|
}
|
|
1245
1255
|
});
|
|
@@ -1248,13 +1258,15 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1248
1258
|
__publicField(this, "getPdfFileViewCtrl", ({
|
|
1249
1259
|
fileKey: _fileKey,
|
|
1250
1260
|
queryField = "file",
|
|
1251
|
-
|
|
1261
|
+
paramField = "file",
|
|
1262
|
+
cachingAgeSeconds = "1y"
|
|
1252
1263
|
} = {}) => {
|
|
1253
1264
|
return (req, res, next) => __async(this, null, function* () {
|
|
1254
|
-
var _a2, _b;
|
|
1255
|
-
let fileKey = _fileKey || (((_a2 = req.
|
|
1265
|
+
var _a2, _b, _c, _d, _e, _f;
|
|
1266
|
+
let fileKey = _fileKey || (((_a2 = req.params) == null ? void 0 : _a2[paramField]) ? decodeURIComponent((_b = req.params) == null ? void 0 : _b[paramField]) : void 0) || (((_c = req.query) == null ? void 0 : _c[queryField]) ? decodeURIComponent((_d = req.query) == null ? void 0 : _d[queryField]) : void 0);
|
|
1256
1267
|
if (!fileKey) {
|
|
1257
|
-
|
|
1268
|
+
(_e = this.logger) == null ? void 0 : _e.warn(req.id, "pdf fileKey is required");
|
|
1269
|
+
next("pdf fileKey is required");
|
|
1258
1270
|
return;
|
|
1259
1271
|
}
|
|
1260
1272
|
try {
|
|
@@ -1273,10 +1285,14 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1273
1285
|
const contentType = mimeTypeMap[ext] || "application/octet-stream";
|
|
1274
1286
|
res.setHeader("Content-Type", contentType);
|
|
1275
1287
|
res.setHeader("Content-Disposition", `inline; filename="${path.basename(fileKey)}"`);
|
|
1276
|
-
res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1277
1288
|
res.setHeader("Content-Length", fileBuffer.length);
|
|
1289
|
+
const cachingAge = !cachingAgeSeconds || typeof cachingAgeSeconds === "number" ? cachingAgeSeconds : getTotalSeconds(cachingAgeSeconds);
|
|
1290
|
+
res.setHeader("Cache-Control", `public, max-age=${cachingAge}`);
|
|
1278
1291
|
res.status(200).send(fileBuffer);
|
|
1279
1292
|
} catch (error) {
|
|
1293
|
+
(_f = this.logger) == null ? void 0 : _f.warn(req.id, "pdf fileKey not found", __spreadValues({
|
|
1294
|
+
fileKey
|
|
1295
|
+
}, this.localstack && { localstack: this.localstack }));
|
|
1280
1296
|
next(`Failed to retrieve pdf file: ${error.message}`);
|
|
1281
1297
|
}
|
|
1282
1298
|
});
|
|
@@ -1284,12 +1300,12 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1284
1300
|
this.maxUploadFileSizeRestriction = maxUploadFileSizeRestriction;
|
|
1285
1301
|
}
|
|
1286
1302
|
streamObjectFile(_0) {
|
|
1287
|
-
return __async(this, arguments, function* (
|
|
1303
|
+
return __async(this, arguments, function* (fileKey, {
|
|
1288
1304
|
Range,
|
|
1289
1305
|
checkFileExists = true,
|
|
1290
1306
|
abortSignal
|
|
1291
1307
|
} = {}) {
|
|
1292
|
-
let normalizedKey = getNormalizedPath(
|
|
1308
|
+
let normalizedKey = getNormalizedPath(fileKey);
|
|
1293
1309
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1294
1310
|
if (checkFileExists) {
|
|
1295
1311
|
const isExists = yield this.fileExists(normalizedKey);
|
|
@@ -1308,13 +1324,12 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1308
1324
|
}
|
|
1309
1325
|
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1310
1326
|
streamVideoFile(_0) {
|
|
1311
|
-
return __async(this, arguments, function* ({
|
|
1312
|
-
filePath,
|
|
1327
|
+
return __async(this, arguments, function* (fileKey, {
|
|
1313
1328
|
Range,
|
|
1314
1329
|
abortSignal
|
|
1315
|
-
}) {
|
|
1330
|
+
} = {}) {
|
|
1316
1331
|
var _a2;
|
|
1317
|
-
let normalizedKey = getNormalizedPath(
|
|
1332
|
+
let normalizedKey = getNormalizedPath(fileKey);
|
|
1318
1333
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1319
1334
|
try {
|
|
1320
1335
|
const cmd = new GetObjectCommand2(__spreadValues({
|
|
@@ -1338,7 +1353,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1338
1353
|
} catch (error) {
|
|
1339
1354
|
(_a2 = this.logger) == null ? void 0 : _a2.warn(this.reqId, "getS3VideoStream error", {
|
|
1340
1355
|
Bucket: this.bucket,
|
|
1341
|
-
|
|
1356
|
+
fileKey: normalizedKey,
|
|
1342
1357
|
Range,
|
|
1343
1358
|
error
|
|
1344
1359
|
});
|
|
@@ -1396,8 +1411,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1396
1411
|
const onClose = () => abort.abort();
|
|
1397
1412
|
req.once("close", onClose);
|
|
1398
1413
|
try {
|
|
1399
|
-
const result = yield this.streamVideoFile({
|
|
1400
|
-
filePath: normalizedKey,
|
|
1414
|
+
const result = yield this.streamVideoFile(normalizedKey, {
|
|
1401
1415
|
Range,
|
|
1402
1416
|
abortSignal: abort.signal
|
|
1403
1417
|
});
|
|
@@ -1462,11 +1476,10 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1462
1476
|
}
|
|
1463
1477
|
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1464
1478
|
getStreamFileCtrl(_0) {
|
|
1465
|
-
return __async(this, arguments, function* ({
|
|
1466
|
-
filePath,
|
|
1479
|
+
return __async(this, arguments, function* (fileKey, {
|
|
1467
1480
|
filename,
|
|
1468
1481
|
forDownloading = false
|
|
1469
|
-
}) {
|
|
1482
|
+
} = {}) {
|
|
1470
1483
|
return (req, res, next) => __async(this, null, function* () {
|
|
1471
1484
|
var _a2, _b;
|
|
1472
1485
|
const abort = new AbortController();
|
|
@@ -1477,7 +1490,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1477
1490
|
(_a3 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _a3.call(stream);
|
|
1478
1491
|
};
|
|
1479
1492
|
req.once("close", onClose);
|
|
1480
|
-
let normalizedKey = getNormalizedPath(
|
|
1493
|
+
let normalizedKey = getNormalizedPath(fileKey);
|
|
1481
1494
|
if (!normalizedKey || normalizedKey === "/") throw new Error("No file key provided");
|
|
1482
1495
|
try {
|
|
1483
1496
|
const isExists = yield this.fileExists(normalizedKey);
|
|
@@ -1506,7 +1519,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1506
1519
|
}
|
|
1507
1520
|
stream.on("error", (err) => {
|
|
1508
1521
|
var _a3, _b2;
|
|
1509
|
-
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "Stream error", {
|
|
1522
|
+
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "Stream error", { fileKey: normalizedKey, error: err });
|
|
1510
1523
|
abort.abort();
|
|
1511
1524
|
(_b2 = stream == null ? void 0 : stream.destroy) == null ? void 0 : _b2.call(stream);
|
|
1512
1525
|
});
|
|
@@ -1526,7 +1539,7 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1526
1539
|
if (isBenignStreamError) {
|
|
1527
1540
|
return;
|
|
1528
1541
|
}
|
|
1529
|
-
(_b = this.logger) == null ? void 0 : _b.error(this.reqId, "Failed to stream file", {
|
|
1542
|
+
(_b = this.logger) == null ? void 0 : _b.error(this.reqId, "Failed to stream file", { fileKey: normalizedKey, error });
|
|
1530
1543
|
if (!res.headersSent) {
|
|
1531
1544
|
next(error);
|
|
1532
1545
|
} else if (!res.writableEnded) {
|
|
@@ -1543,33 +1556,30 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1543
1556
|
}
|
|
1544
1557
|
// todo: LOCALSTACK SANITY CHECKED - WORKING WELL, DON'T TOUCH!
|
|
1545
1558
|
getStreamZipFileCtr(_0) {
|
|
1546
|
-
return __async(this, arguments, function* ({
|
|
1547
|
-
filePath,
|
|
1559
|
+
return __async(this, arguments, function* (fileKey, {
|
|
1548
1560
|
filename: _filename,
|
|
1549
1561
|
compressionLevel = 5
|
|
1550
|
-
}) {
|
|
1562
|
+
} = {}) {
|
|
1551
1563
|
return (req, res, next) => __async(this, null, function* () {
|
|
1552
1564
|
var _a2, _b, _c, _d, _e;
|
|
1553
|
-
const filePaths = [].concat(filePath).map((filePath2) => getNormalizedPath(filePath2)).filter((v) => v && v !== "/");
|
|
1554
|
-
if (!filePaths.length) {
|
|
1555
|
-
throw new Error("No file keys provided");
|
|
1556
|
-
}
|
|
1557
|
-
let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
|
|
1558
|
-
filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
|
|
1559
1565
|
const abort = new AbortController();
|
|
1560
|
-
const onClose = () =>
|
|
1561
|
-
abort.abort();
|
|
1562
|
-
};
|
|
1563
|
-
req.once("close", onClose);
|
|
1566
|
+
const onClose = () => abort.abort();
|
|
1564
1567
|
try {
|
|
1565
|
-
|
|
1566
|
-
|
|
1568
|
+
const fileKeys = [].concat(fileKey).map((fileKey2) => getNormalizedPath(fileKey2)).filter((v) => v && v !== "/");
|
|
1569
|
+
if (!fileKeys.length) {
|
|
1570
|
+
throw new Error("No file keys provided");
|
|
1571
|
+
}
|
|
1572
|
+
let filename = _filename || (/* @__PURE__ */ new Date()).toISOString();
|
|
1573
|
+
filename = filename.endsWith(".zip") ? filename : `${filename}.zip`;
|
|
1574
|
+
req.once("close", onClose);
|
|
1575
|
+
(_a2 = this.logger) == null ? void 0 : _a2.info(this.reqId, "Starting parallel file download...", { fileCount: fileKeys.length });
|
|
1576
|
+
const downloadPromises = fileKeys.map((fileKey2) => __async(this, null, function* () {
|
|
1567
1577
|
var _a3, _b2, _c2;
|
|
1568
1578
|
try {
|
|
1569
1579
|
if (abort.signal.aborted) return null;
|
|
1570
|
-
const stream = yield this.streamObjectFile(
|
|
1580
|
+
const stream = yield this.streamObjectFile(fileKey2, { abortSignal: abort.signal });
|
|
1571
1581
|
if (!stream) {
|
|
1572
|
-
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", {
|
|
1582
|
+
(_a3 = this.logger) == null ? void 0 : _a3.warn(this.reqId, "File not found", { fileKey: fileKey2 });
|
|
1573
1583
|
return null;
|
|
1574
1584
|
}
|
|
1575
1585
|
const chunks = [];
|
|
@@ -1593,14 +1603,14 @@ var S3Stream = class _S3Stream extends S3File {
|
|
|
1593
1603
|
}
|
|
1594
1604
|
}
|
|
1595
1605
|
const buffer = Buffer3.concat(chunks);
|
|
1596
|
-
const fileName =
|
|
1606
|
+
const fileName = fileKey2.split("/").pop() || fileKey2;
|
|
1597
1607
|
(_b2 = this.logger) == null ? void 0 : _b2.debug(this.reqId, "File downloaded", {
|
|
1598
|
-
|
|
1608
|
+
fileKey: fileKey2,
|
|
1599
1609
|
sizeMB: (buffer.length / (1024 * 1024)).toFixed(2)
|
|
1600
1610
|
});
|
|
1601
|
-
return { buffer, name: fileName, path:
|
|
1611
|
+
return { buffer, name: fileName, path: fileKey2 };
|
|
1602
1612
|
} catch (error2) {
|
|
1603
|
-
(_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", {
|
|
1613
|
+
(_c2 = this.logger) == null ? void 0 : _c2.warn(this.reqId, "Failed to download file", { fileKey: fileKey2, error: error2 });
|
|
1604
1614
|
return null;
|
|
1605
1615
|
}
|
|
1606
1616
|
}));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hdriel/aws-utils",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "Simplified AWS SDK (v3) utilities for S3 (upload, download, streaming) with TypeScript support",
|
|
5
5
|
"author": "Hadriel Benjo (https://github.com/hdriel)",
|
|
6
6
|
"type": "module",
|