@kevisual/oss 0.0.17 → 0.0.19
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/dist/index.d.ts +15 -2
- package/dist/index.js +67 -20
- package/dist/services.d.ts +35 -130
- package/dist/services.js +60 -77
- package/package.json +2 -3
- package/src/index.ts +3 -1
- package/src/s3/core.ts +72 -23
- package/src/s3/type.ts +8 -0
- package/src/services/index.ts +1 -1
- package/src/util/download.ts +69 -69
- package/src/core/copy-object.ts +0 -26
- package/src/core/core.ts +0 -244
- package/src/core/type.ts +0 -87
package/dist/services.js
CHANGED
|
@@ -36456,15 +36456,21 @@ class OssBase {
|
|
|
36456
36456
|
contentLength = Buffer.byteLength(putData);
|
|
36457
36457
|
}
|
|
36458
36458
|
} else {
|
|
36459
|
-
putData = data;
|
|
36460
36459
|
if (!contentLength) {
|
|
36461
|
-
|
|
36460
|
+
const chunks = [];
|
|
36461
|
+
for await (const chunk of data) {
|
|
36462
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
36463
|
+
}
|
|
36464
|
+
putData = Buffer.concat(chunks);
|
|
36465
|
+
contentLength = putData.length;
|
|
36466
|
+
} else {
|
|
36467
|
+
putData = data;
|
|
36462
36468
|
}
|
|
36463
36469
|
}
|
|
36464
36470
|
if (opts?.check) {
|
|
36465
36471
|
const obj = await this.statObject(objectName, true);
|
|
36466
36472
|
if (obj) {
|
|
36467
|
-
const omitMeta = ["size", "
|
|
36473
|
+
const omitMeta = ["size", "content-type", "cache-control", "app-source"];
|
|
36468
36474
|
const objMeta = JSON.parse(JSON.stringify(omit(obj.metaData, omitMeta)));
|
|
36469
36475
|
metaData = {
|
|
36470
36476
|
...objMeta,
|
|
@@ -36493,22 +36499,41 @@ class OssBase {
|
|
|
36493
36499
|
};
|
|
36494
36500
|
}
|
|
36495
36501
|
async fPutObject(objectName, filePath, metaData) {
|
|
36496
|
-
const fileStream = fs.createReadStream(filePath);
|
|
36497
36502
|
const stat = fs.statSync(filePath);
|
|
36498
36503
|
const { standardHeaders, customMetadata } = extractStandardHeaders(metaData || {});
|
|
36499
|
-
const
|
|
36500
|
-
|
|
36501
|
-
|
|
36502
|
-
|
|
36503
|
-
|
|
36504
|
-
|
|
36505
|
-
|
|
36506
|
-
|
|
36507
|
-
|
|
36508
|
-
|
|
36509
|
-
|
|
36510
|
-
|
|
36511
|
-
|
|
36504
|
+
const THRESHOLD = 5 * 1024 * 1024;
|
|
36505
|
+
let command;
|
|
36506
|
+
if (stat.size < THRESHOLD) {
|
|
36507
|
+
const fileBuffer = await fs.promises.readFile(filePath);
|
|
36508
|
+
command = new import_client_s3.PutObjectCommand({
|
|
36509
|
+
Bucket: this.bucketName,
|
|
36510
|
+
Key: `${this.prefix}${objectName}`,
|
|
36511
|
+
Body: fileBuffer,
|
|
36512
|
+
ContentLength: fileBuffer.length,
|
|
36513
|
+
ContentType: standardHeaders.ContentType || getContentType(filePath),
|
|
36514
|
+
CacheControl: standardHeaders.CacheControl,
|
|
36515
|
+
ContentDisposition: standardHeaders.ContentDisposition,
|
|
36516
|
+
ContentEncoding: standardHeaders.ContentEncoding,
|
|
36517
|
+
ContentLanguage: standardHeaders.ContentLanguage,
|
|
36518
|
+
Expires: standardHeaders.Expires,
|
|
36519
|
+
Metadata: customMetadata
|
|
36520
|
+
});
|
|
36521
|
+
} else {
|
|
36522
|
+
const fileStream = fs.createReadStream(filePath);
|
|
36523
|
+
command = new import_client_s3.PutObjectCommand({
|
|
36524
|
+
Bucket: this.bucketName,
|
|
36525
|
+
Key: `${this.prefix}${objectName}`,
|
|
36526
|
+
Body: fileStream,
|
|
36527
|
+
ContentLength: stat.size,
|
|
36528
|
+
ContentType: standardHeaders.ContentType || getContentType(filePath),
|
|
36529
|
+
CacheControl: standardHeaders.CacheControl,
|
|
36530
|
+
ContentDisposition: standardHeaders.ContentDisposition,
|
|
36531
|
+
ContentEncoding: standardHeaders.ContentEncoding,
|
|
36532
|
+
ContentLanguage: standardHeaders.ContentLanguage,
|
|
36533
|
+
Expires: standardHeaders.Expires,
|
|
36534
|
+
Metadata: customMetadata
|
|
36535
|
+
});
|
|
36536
|
+
}
|
|
36512
36537
|
const response = await this.client.send(command);
|
|
36513
36538
|
return {
|
|
36514
36539
|
etag: response.ETag?.replace(/"/g, "") || "",
|
|
@@ -36531,6 +36556,7 @@ class OssBase {
|
|
|
36531
36556
|
const prefix = `${this.prefix}${objectName}`;
|
|
36532
36557
|
const results = [];
|
|
36533
36558
|
let continuationToken;
|
|
36559
|
+
const getMeta = opts?.getMeta ?? false;
|
|
36534
36560
|
do {
|
|
36535
36561
|
const command = new import_client_s3.ListObjectsV2Command({
|
|
36536
36562
|
Bucket: this.bucketName,
|
|
@@ -36543,12 +36569,19 @@ class OssBase {
|
|
|
36543
36569
|
const response = await this.client.send(command);
|
|
36544
36570
|
if (response.Contents) {
|
|
36545
36571
|
for (const item of response.Contents) {
|
|
36546
|
-
|
|
36572
|
+
const result = {
|
|
36547
36573
|
name: item.Key || "",
|
|
36548
36574
|
size: item.Size || 0,
|
|
36549
36575
|
lastModified: item.LastModified || new Date,
|
|
36550
36576
|
etag: item.ETag?.replace(/"/g, "") || ""
|
|
36551
|
-
}
|
|
36577
|
+
};
|
|
36578
|
+
if (getMeta) {
|
|
36579
|
+
const stat = await this.statObject(item.Key || "", false);
|
|
36580
|
+
if (stat?.metaData) {
|
|
36581
|
+
result.metaData = stat.metaData;
|
|
36582
|
+
}
|
|
36583
|
+
}
|
|
36584
|
+
results.push(result);
|
|
36552
36585
|
}
|
|
36553
36586
|
}
|
|
36554
36587
|
if (response.CommonPrefixes && !opts?.recursive) {
|
|
@@ -36571,6 +36604,13 @@ class OssBase {
|
|
|
36571
36604
|
});
|
|
36572
36605
|
const response = await this.client.send(command);
|
|
36573
36606
|
return {
|
|
36607
|
+
standardHeaders: {
|
|
36608
|
+
contentType: response.ContentType,
|
|
36609
|
+
cacheControl: response.CacheControl,
|
|
36610
|
+
contentDisposition: response.ContentDisposition,
|
|
36611
|
+
contentEncoding: response.ContentEncoding,
|
|
36612
|
+
contentLanguage: response.ContentLanguage
|
|
36613
|
+
},
|
|
36574
36614
|
size: response.ContentLength || 0,
|
|
36575
36615
|
etag: response.ETag?.replace?.(/"/g, "") || "",
|
|
36576
36616
|
lastModified: response.LastModified || new Date,
|
|
@@ -36578,11 +36618,11 @@ class OssBase {
|
|
|
36578
36618
|
versionId: response.VersionId || null
|
|
36579
36619
|
};
|
|
36580
36620
|
} catch (e) {
|
|
36581
|
-
console.error("statObject error", e);
|
|
36582
36621
|
const isNotFound = e.name === "NotFound" || e.name === "NoSuchBucket" || e.name === "NoSuchKey" || e.code === "NotFound" || e.code === "NoSuchBucket" || e.code === "NoSuchKey" || e.$metadata?.httpStatusCode === 404;
|
|
36583
36622
|
if (checkFile && isNotFound) {
|
|
36584
36623
|
return null;
|
|
36585
36624
|
}
|
|
36625
|
+
console.error("statObject error", e);
|
|
36586
36626
|
throw e;
|
|
36587
36627
|
}
|
|
36588
36628
|
}
|
|
@@ -36723,69 +36763,12 @@ class ConfigOssService extends OssBase {
|
|
|
36723
36763
|
return { list: listKeys, keys, keyEtagMap };
|
|
36724
36764
|
}
|
|
36725
36765
|
}
|
|
36726
|
-
// src/util/download.ts
|
|
36727
|
-
import fs2 from "node:fs";
|
|
36728
|
-
import path2 from "node:path";
|
|
36729
|
-
var viewableExtensions = ["jpg", "jpeg", "png", "gif", "svg", "webp", "mp4", "webm", "mp3", "wav", "ogg", "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx"];
|
|
36730
|
-
var filterMetaDataKeys = (metaData, clearKeys = []) => {
|
|
36731
|
-
const keys = Object.keys(metaData);
|
|
36732
|
-
const removeKeys = ["password", "accesskey", "secretkey", ...clearKeys];
|
|
36733
|
-
const filteredKeys = keys.filter((key) => !removeKeys.includes(key));
|
|
36734
|
-
return filteredKeys.reduce((acc, key) => {
|
|
36735
|
-
acc[key] = metaData[key];
|
|
36736
|
-
return acc;
|
|
36737
|
-
}, {});
|
|
36738
|
-
};
|
|
36739
|
-
var NotFoundFile = (res, msg, code = 404) => {
|
|
36740
|
-
res.writeHead(code, { "Content-Type": "text/plain" });
|
|
36741
|
-
res.end(msg || "Not Found File");
|
|
36742
|
-
return;
|
|
36743
|
-
};
|
|
36744
|
-
var sendObject = async ({ res, objectName, client, isDownload = false }) => {
|
|
36745
|
-
let stat;
|
|
36746
|
-
try {
|
|
36747
|
-
stat = await client.statObject(objectName);
|
|
36748
|
-
} catch (e) {} finally {
|
|
36749
|
-
if (!stat || stat.size === 0) {
|
|
36750
|
-
return NotFoundFile(res);
|
|
36751
|
-
}
|
|
36752
|
-
const contentLength = stat.size;
|
|
36753
|
-
const etag = stat.etag;
|
|
36754
|
-
const lastModified = stat.lastModified.toISOString();
|
|
36755
|
-
const filename = objectName.split("/").pop() || "no-file-name-download";
|
|
36756
|
-
const fileExtension = filename.split(".").pop()?.toLowerCase() || "";
|
|
36757
|
-
const filteredMetaData = filterMetaDataKeys(stat.metaData, ["size", "etag", "last-modified"]);
|
|
36758
|
-
const contentDisposition = viewableExtensions.includes(fileExtension) && !isDownload ? "inline" : `attachment; filename="${filename}"`;
|
|
36759
|
-
res.writeHead(200, {
|
|
36760
|
-
"Content-Length": contentLength,
|
|
36761
|
-
etag,
|
|
36762
|
-
"last-modified": lastModified,
|
|
36763
|
-
"Content-Disposition": contentDisposition,
|
|
36764
|
-
...filteredMetaData
|
|
36765
|
-
});
|
|
36766
|
-
const objectStream = await client.getObject(objectName);
|
|
36767
|
-
objectStream.pipe(res, { end: true });
|
|
36768
|
-
}
|
|
36769
|
-
};
|
|
36770
|
-
var downloadObject = async ({ objectName, client, filePath }) => {
|
|
36771
|
-
const objectStream = await client.getObject(objectName);
|
|
36772
|
-
const dir = path2.dirname(filePath);
|
|
36773
|
-
if (!fs2.existsSync(dir)) {
|
|
36774
|
-
fs2.mkdirSync(dir, { recursive: true });
|
|
36775
|
-
}
|
|
36776
|
-
objectStream.pipe(fs2.createWriteStream(filePath));
|
|
36777
|
-
return objectStream;
|
|
36778
|
-
};
|
|
36779
36766
|
export {
|
|
36780
36767
|
standardHeaderKeys,
|
|
36781
|
-
sendObject,
|
|
36782
36768
|
hashSringify,
|
|
36783
36769
|
hash,
|
|
36784
36770
|
getContentType,
|
|
36785
|
-
filterMetaDataKeys,
|
|
36786
36771
|
extractStandardHeaders,
|
|
36787
|
-
downloadObject,
|
|
36788
36772
|
OssBase,
|
|
36789
|
-
NotFoundFile,
|
|
36790
36773
|
ConfigOssService
|
|
36791
36774
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kevisual/oss",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.19",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "bun run bun.config.ts"
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
"bun-plugin-dts": "^0.3.0",
|
|
22
22
|
"dotenv": "^17.2.3",
|
|
23
23
|
"es-toolkit": "^1.44.0",
|
|
24
|
-
"fast-glob": "^3.3.3"
|
|
25
|
-
"minio": "^8.0.6"
|
|
24
|
+
"fast-glob": "^3.3.3"
|
|
26
25
|
},
|
|
27
26
|
"exports": {
|
|
28
27
|
".": {
|
package/src/index.ts
CHANGED
package/src/s3/core.ts
CHANGED
|
@@ -127,9 +127,16 @@ export class OssBase implements OssBaseOperation {
|
|
|
127
127
|
contentLength = Buffer.byteLength(putData);
|
|
128
128
|
}
|
|
129
129
|
} else {
|
|
130
|
-
|
|
130
|
+
// Stream 上传:自动读取到 Buffer 以获取 contentLength
|
|
131
131
|
if (!contentLength) {
|
|
132
|
-
|
|
132
|
+
const chunks: Buffer[] = [];
|
|
133
|
+
for await (const chunk of data as Readable) {
|
|
134
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
135
|
+
}
|
|
136
|
+
putData = Buffer.concat(chunks);
|
|
137
|
+
contentLength = putData.length;
|
|
138
|
+
} else {
|
|
139
|
+
putData = data as Readable;
|
|
133
140
|
}
|
|
134
141
|
}
|
|
135
142
|
|
|
@@ -137,7 +144,7 @@ export class OssBase implements OssBaseOperation {
|
|
|
137
144
|
if (opts?.check) {
|
|
138
145
|
const obj = await this.statObject(objectName, true);
|
|
139
146
|
if (obj) {
|
|
140
|
-
const omitMeta = ['size', '
|
|
147
|
+
const omitMeta = ['size', 'content-type', 'cache-control', 'app-source'];
|
|
141
148
|
const objMeta = JSON.parse(JSON.stringify(omit(obj.metaData, omitMeta)));
|
|
142
149
|
metaData = {
|
|
143
150
|
...objMeta,
|
|
@@ -180,24 +187,45 @@ export class OssBase implements OssBaseOperation {
|
|
|
180
187
|
filePath: string,
|
|
181
188
|
metaData?: ItemBucketMetadata,
|
|
182
189
|
): Promise<UploadedObjectInfo> {
|
|
183
|
-
const fileStream = fs.createReadStream(filePath);
|
|
184
190
|
const stat = fs.statSync(filePath);
|
|
185
|
-
|
|
186
191
|
const { standardHeaders, customMetadata } = extractStandardHeaders(metaData || {});
|
|
187
192
|
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
193
|
+
const THRESHOLD = 5 * 1024 * 1024; // 5MB
|
|
194
|
+
let command: PutObjectCommand;
|
|
195
|
+
|
|
196
|
+
if (stat.size < THRESHOLD) {
|
|
197
|
+
// 小文件:读取到内存再上传,避免流式传输导致的 IncompleteBody 错误
|
|
198
|
+
const fileBuffer = await fs.promises.readFile(filePath);
|
|
199
|
+
command = new PutObjectCommand({
|
|
200
|
+
Bucket: this.bucketName,
|
|
201
|
+
Key: `${this.prefix}${objectName}`,
|
|
202
|
+
Body: fileBuffer,
|
|
203
|
+
ContentLength: fileBuffer.length,
|
|
204
|
+
ContentType: standardHeaders.ContentType || getContentType(filePath),
|
|
205
|
+
CacheControl: standardHeaders.CacheControl,
|
|
206
|
+
ContentDisposition: standardHeaders.ContentDisposition,
|
|
207
|
+
ContentEncoding: standardHeaders.ContentEncoding,
|
|
208
|
+
ContentLanguage: standardHeaders.ContentLanguage,
|
|
209
|
+
Expires: standardHeaders.Expires,
|
|
210
|
+
Metadata: customMetadata,
|
|
211
|
+
});
|
|
212
|
+
} else {
|
|
213
|
+
// 大文件:使用流式上传
|
|
214
|
+
const fileStream = fs.createReadStream(filePath);
|
|
215
|
+
command = new PutObjectCommand({
|
|
216
|
+
Bucket: this.bucketName,
|
|
217
|
+
Key: `${this.prefix}${objectName}`,
|
|
218
|
+
Body: fileStream,
|
|
219
|
+
ContentLength: stat.size,
|
|
220
|
+
ContentType: standardHeaders.ContentType || getContentType(filePath),
|
|
221
|
+
CacheControl: standardHeaders.CacheControl,
|
|
222
|
+
ContentDisposition: standardHeaders.ContentDisposition,
|
|
223
|
+
ContentEncoding: standardHeaders.ContentEncoding,
|
|
224
|
+
ContentLanguage: standardHeaders.ContentLanguage,
|
|
225
|
+
Expires: standardHeaders.Expires,
|
|
226
|
+
Metadata: customMetadata,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
201
229
|
|
|
202
230
|
const response = await this.client.send(command);
|
|
203
231
|
return {
|
|
@@ -230,12 +258,12 @@ export class OssBase implements OssBaseOperation {
|
|
|
230
258
|
*/
|
|
231
259
|
async listObjects<IS_FILE = false>(
|
|
232
260
|
objectName: string,
|
|
233
|
-
opts?: { recursive?: boolean; startAfter?: string; maxKeys?: number },
|
|
261
|
+
opts?: { recursive?: boolean; startAfter?: string; maxKeys?: number, getMeta?: boolean },
|
|
234
262
|
): Promise<IS_FILE extends true ? ListFileObject[] : ListObjectResult[]> {
|
|
235
263
|
const prefix = `${this.prefix}${objectName}`;
|
|
236
264
|
const results: ListObjectResult[] = [];
|
|
237
265
|
let continuationToken: string | undefined;
|
|
238
|
-
|
|
266
|
+
const getMeta = opts?.getMeta ?? false;
|
|
239
267
|
do {
|
|
240
268
|
const command = new ListObjectsV2Command({
|
|
241
269
|
Bucket: this.bucketName,
|
|
@@ -251,12 +279,19 @@ export class OssBase implements OssBaseOperation {
|
|
|
251
279
|
// 处理文件对象
|
|
252
280
|
if (response.Contents) {
|
|
253
281
|
for (const item of response.Contents) {
|
|
254
|
-
|
|
282
|
+
const result: ListFileObject = {
|
|
255
283
|
name: item.Key || '',
|
|
256
284
|
size: item.Size || 0,
|
|
257
285
|
lastModified: item.LastModified || new Date(),
|
|
258
286
|
etag: item.ETag?.replace(/"/g, '') || '',
|
|
259
|
-
}
|
|
287
|
+
}
|
|
288
|
+
if (getMeta) {
|
|
289
|
+
const stat = await this.statObject(item.Key || '', false);
|
|
290
|
+
if (stat?.metaData) {
|
|
291
|
+
result.metaData = stat.metaData;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
results.push(result);
|
|
260
295
|
}
|
|
261
296
|
}
|
|
262
297
|
|
|
@@ -290,6 +325,13 @@ export class OssBase implements OssBaseOperation {
|
|
|
290
325
|
const response = await this.client.send(command);
|
|
291
326
|
|
|
292
327
|
return {
|
|
328
|
+
standardHeaders: {
|
|
329
|
+
contentType: response.ContentType,
|
|
330
|
+
cacheControl: response.CacheControl,
|
|
331
|
+
contentDisposition: response.ContentDisposition,
|
|
332
|
+
contentEncoding: response.ContentEncoding,
|
|
333
|
+
contentLanguage: response.ContentLanguage,
|
|
334
|
+
},
|
|
293
335
|
size: response.ContentLength || 0,
|
|
294
336
|
etag: response.ETag?.replace?.(/"/g, '') || '',
|
|
295
337
|
lastModified: response.LastModified || new Date(),
|
|
@@ -297,7 +339,6 @@ export class OssBase implements OssBaseOperation {
|
|
|
297
339
|
versionId: response.VersionId || null,
|
|
298
340
|
};
|
|
299
341
|
} catch (e: any) {
|
|
300
|
-
console.error('statObject error', e);
|
|
301
342
|
// 检查是否是 404 错误 - 支持多种 S3 兼容存储的错误格式
|
|
302
343
|
const isNotFound =
|
|
303
344
|
e.name === 'NotFound' ||
|
|
@@ -311,6 +352,7 @@ export class OssBase implements OssBaseOperation {
|
|
|
311
352
|
if (checkFile && isNotFound) {
|
|
312
353
|
return null;
|
|
313
354
|
}
|
|
355
|
+
console.error('statObject error', e);
|
|
314
356
|
throw e;
|
|
315
357
|
}
|
|
316
358
|
}
|
|
@@ -438,4 +480,11 @@ export class OssBase implements OssBaseOperation {
|
|
|
438
480
|
...opts,
|
|
439
481
|
});
|
|
440
482
|
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
export const getStram = (data: GetObjectCommandOutput) => {
|
|
486
|
+
if (data.Body) {
|
|
487
|
+
return data.Body as Readable;
|
|
488
|
+
}
|
|
489
|
+
throw new Error('Object body is empty');
|
|
441
490
|
}
|
package/src/s3/type.ts
CHANGED
|
@@ -17,6 +17,13 @@ export type StatObjectResult = {
|
|
|
17
17
|
lastModified: Date;
|
|
18
18
|
metaData: ItemBucketMetadata;
|
|
19
19
|
versionId?: string | null;
|
|
20
|
+
standardHeaders: {
|
|
21
|
+
contentType?: string;
|
|
22
|
+
cacheControl?: string;
|
|
23
|
+
contentDisposition?: string;
|
|
24
|
+
contentEncoding?: string;
|
|
25
|
+
contentLanguage?: string;
|
|
26
|
+
}
|
|
20
27
|
};
|
|
21
28
|
|
|
22
29
|
export type ListFileObject = {
|
|
@@ -24,6 +31,7 @@ export type ListFileObject = {
|
|
|
24
31
|
size: number;
|
|
25
32
|
lastModified: Date;
|
|
26
33
|
etag: string;
|
|
34
|
+
metaData?: ItemBucketMetadata;
|
|
27
35
|
};
|
|
28
36
|
|
|
29
37
|
export type ListDirectoryObject = {
|
package/src/services/index.ts
CHANGED
package/src/util/download.ts
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import { ServerResponse } from 'node:http';
|
|
2
|
-
import { BucketItemStat } from 'minio';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
1
|
+
// import { ServerResponse } from 'node:http';
|
|
2
|
+
// import { BucketItemStat } from 'minio';
|
|
3
|
+
// import fs from 'node:fs';
|
|
4
|
+
// import path from 'node:path';
|
|
5
5
|
|
|
6
|
-
const viewableExtensions = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'mp4', 'webm', 'mp3', 'wav', 'ogg', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'];
|
|
7
|
-
import { OssBase } from '../index.ts';
|
|
8
|
-
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
export const filterMetaDataKeys = (metaData: Record<string, string>, clearKeys: string[] = []) => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
};
|
|
24
|
-
type SendObjectOptions = {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
export const NotFoundFile = (res: ServerResponse, msg?: string, code = 404) => {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
};
|
|
35
|
-
export const sendObject = async ({ res, objectName, client, isDownload = false }: SendObjectOptions) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
6
|
+
// const viewableExtensions = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'mp4', 'webm', 'mp3', 'wav', 'ogg', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx'];
|
|
7
|
+
// import { OssBase } from '../index.ts';
|
|
8
|
+
// /**
|
|
9
|
+
// * 过滤 metaData 中的 key, 去除 password, accesskey, secretkey,
|
|
10
|
+
// * 并返回过滤后的 metaData
|
|
11
|
+
// * @param metaData
|
|
12
|
+
// * @returns
|
|
13
|
+
// */
|
|
14
|
+
// export const filterMetaDataKeys = (metaData: Record<string, string>, clearKeys: string[] = []) => {
|
|
15
|
+
// const keys = Object.keys(metaData);
|
|
16
|
+
// // remove X-Amz- meta data
|
|
17
|
+
// const removeKeys = ['password', 'accesskey', 'secretkey', ...clearKeys];
|
|
18
|
+
// const filteredKeys = keys.filter((key) => !removeKeys.includes(key));
|
|
19
|
+
// return filteredKeys.reduce((acc, key) => {
|
|
20
|
+
// acc[key] = metaData[key];
|
|
21
|
+
// return acc;
|
|
22
|
+
// }, {} as Record<string, string>);
|
|
23
|
+
// };
|
|
24
|
+
// type SendObjectOptions = {
|
|
25
|
+
// res: ServerResponse;
|
|
26
|
+
// client: OssBase;
|
|
27
|
+
// objectName: string;
|
|
28
|
+
// isDownload?: boolean;
|
|
29
|
+
// };
|
|
30
|
+
// export const NotFoundFile = (res: ServerResponse, msg?: string, code = 404) => {
|
|
31
|
+
// res.writeHead(code, { 'Content-Type': 'text/plain' });
|
|
32
|
+
// res.end(msg || 'Not Found File');
|
|
33
|
+
// return;
|
|
34
|
+
// };
|
|
35
|
+
// export const sendObject = async ({ res, objectName, client, isDownload = false }: SendObjectOptions) => {
|
|
36
|
+
// let stat: BucketItemStat;
|
|
37
|
+
// try {
|
|
38
|
+
// stat = await client.statObject(objectName);
|
|
39
|
+
// } catch (e) {
|
|
40
|
+
// } finally {
|
|
41
|
+
// if (!stat || stat.size === 0) {
|
|
42
|
+
// return NotFoundFile(res);
|
|
43
|
+
// }
|
|
44
|
+
// const contentLength = stat.size;
|
|
45
|
+
// const etag = stat.etag;
|
|
46
|
+
// const lastModified = stat.lastModified.toISOString();
|
|
47
|
+
// const filename = objectName.split('/').pop() || 'no-file-name-download'; // Extract filename from objectName
|
|
48
|
+
// const fileExtension = filename.split('.').pop()?.toLowerCase() || '';
|
|
49
|
+
// const filteredMetaData = filterMetaDataKeys(stat.metaData, ['size', 'etag', 'last-modified']);
|
|
50
|
+
// const contentDisposition = viewableExtensions.includes(fileExtension) && !isDownload ? 'inline' : `attachment; filename="${filename}"`;
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
// res.writeHead(200, {
|
|
53
|
+
// 'Content-Length': contentLength,
|
|
54
|
+
// etag,
|
|
55
|
+
// 'last-modified': lastModified,
|
|
56
|
+
// 'Content-Disposition': contentDisposition,
|
|
57
|
+
// ...filteredMetaData,
|
|
58
|
+
// });
|
|
59
|
+
// const objectStream = await client.getObject(objectName);
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
};
|
|
61
|
+
// objectStream.pipe(res, { end: true });
|
|
62
|
+
// }
|
|
63
|
+
// };
|
|
64
64
|
|
|
65
|
-
export const downloadObject = async ({ objectName, client, filePath }: Pick<SendObjectOptions, 'objectName' | 'client'> & { filePath: string }) => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
65
|
+
// export const downloadObject = async ({ objectName, client, filePath }: Pick<SendObjectOptions, 'objectName' | 'client'> & { filePath: string }) => {
|
|
66
|
+
// const objectStream = await client.getObject(objectName);
|
|
67
|
+
// const dir = path.dirname(filePath);
|
|
68
|
+
// if (!fs.existsSync(dir)) {
|
|
69
|
+
// fs.mkdirSync(dir, { recursive: true });
|
|
70
|
+
// }
|
|
71
|
+
// objectStream.pipe(fs.createWriteStream(filePath));
|
|
72
|
+
// return objectStream;
|
|
73
|
+
// };
|
package/src/core/copy-object.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Client, CopyDestinationOptions, CopySourceOptions } from 'minio';
|
|
2
|
-
|
|
3
|
-
type CopyObjectOpts = {
|
|
4
|
-
bucketName: string;
|
|
5
|
-
newMetadata: Record<string, string>;
|
|
6
|
-
objectName: string;
|
|
7
|
-
client: Client;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* 复制对象 REPLACE 替换
|
|
11
|
-
* @param param0
|
|
12
|
-
* @returns
|
|
13
|
-
*/
|
|
14
|
-
export const copyObject = async ({ bucketName, newMetadata, objectName, client }: CopyObjectOpts) => {
|
|
15
|
-
const source = new CopySourceOptions({ Bucket: bucketName, Object: objectName });
|
|
16
|
-
const stat = await client.statObject(bucketName, objectName);
|
|
17
|
-
const sourceMetadata = stat.metaData;
|
|
18
|
-
const destination = new CopyDestinationOptions({
|
|
19
|
-
Bucket: bucketName,
|
|
20
|
-
Object: objectName,
|
|
21
|
-
UserMetadata: { ...sourceMetadata, ...newMetadata },
|
|
22
|
-
MetadataDirective: 'REPLACE',
|
|
23
|
-
});
|
|
24
|
-
const copyResult = await client.copyObject(source, destination);
|
|
25
|
-
return copyResult;
|
|
26
|
-
};
|