@kevisual/oss 0.0.17 → 0.0.18

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 CHANGED
@@ -15,6 +15,13 @@ type StatObjectResult = {
15
15
  lastModified: Date;
16
16
  metaData: ItemBucketMetadata;
17
17
  versionId?: string | null;
18
+ standardHeaders: {
19
+ contentType?: string;
20
+ cacheControl?: string;
21
+ contentDisposition?: string;
22
+ contentEncoding?: string;
23
+ contentLanguage?: string;
24
+ };
18
25
  };
19
26
  type ListFileObject = {
20
27
  name: string;
@@ -129,6 +136,9 @@ interface OssBaseOperation {
129
136
  */
130
137
  replaceObject?(objectName: string, meta: ItemBucketMetadata): Promise<any>;
131
138
  }
139
+ interface OssService extends OssBaseOperation {
140
+ owner: string;
141
+ }
132
142
 
133
143
  type OssBaseOptions<T = {
134
144
  [key: string]: any;
@@ -260,6 +270,7 @@ declare class OssBase implements OssBaseOperation {
260
270
  opts: Partial<OssBaseOptions<U>>;
261
271
  }): T;
262
272
  }
273
+ declare const getStram: (data: GetObjectCommandOutput) => Readable;
263
274
 
264
- export { OssBase };
265
- export type { OssBaseOptions };
275
+ export { OssBase, getStram };
276
+ export type { ItemBucketMetadata, ListDirectoryObject, ListFileObject, ListObjectResult, OssBaseOperation, OssBaseOptions, OssService, StatObjectResult, UploadedObjectInfo };
package/dist/index.js CHANGED
@@ -36452,15 +36452,21 @@ class OssBase {
36452
36452
  contentLength = Buffer.byteLength(putData);
36453
36453
  }
36454
36454
  } else {
36455
- putData = data;
36456
36455
  if (!contentLength) {
36457
- throw new Error("Stream upload requires size parameter");
36456
+ const chunks = [];
36457
+ for await (const chunk of data) {
36458
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
36459
+ }
36460
+ putData = Buffer.concat(chunks);
36461
+ contentLength = putData.length;
36462
+ } else {
36463
+ putData = data;
36458
36464
  }
36459
36465
  }
36460
36466
  if (opts?.check) {
36461
36467
  const obj = await this.statObject(objectName, true);
36462
36468
  if (obj) {
36463
- const omitMeta = ["size", "Content-Type", "Cache-Control", "app-source"];
36469
+ const omitMeta = ["size", "content-type", "cache-control", "app-source"];
36464
36470
  const objMeta = JSON.parse(JSON.stringify(omit(obj.metaData, omitMeta)));
36465
36471
  metaData = {
36466
36472
  ...objMeta,
@@ -36489,22 +36495,41 @@ class OssBase {
36489
36495
  };
36490
36496
  }
36491
36497
  async fPutObject(objectName, filePath, metaData) {
36492
- const fileStream = fs.createReadStream(filePath);
36493
36498
  const stat = fs.statSync(filePath);
36494
36499
  const { standardHeaders, customMetadata } = extractStandardHeaders(metaData || {});
36495
- const command = new import_client_s3.PutObjectCommand({
36496
- Bucket: this.bucketName,
36497
- Key: `${this.prefix}${objectName}`,
36498
- Body: fileStream,
36499
- ContentLength: stat.size,
36500
- ContentType: standardHeaders.ContentType || getContentType(filePath),
36501
- CacheControl: standardHeaders.CacheControl,
36502
- ContentDisposition: standardHeaders.ContentDisposition,
36503
- ContentEncoding: standardHeaders.ContentEncoding,
36504
- ContentLanguage: standardHeaders.ContentLanguage,
36505
- Expires: standardHeaders.Expires,
36506
- Metadata: customMetadata
36507
- });
36500
+ const THRESHOLD = 5 * 1024 * 1024;
36501
+ let command;
36502
+ if (stat.size < THRESHOLD) {
36503
+ const fileBuffer = await fs.promises.readFile(filePath);
36504
+ command = new import_client_s3.PutObjectCommand({
36505
+ Bucket: this.bucketName,
36506
+ Key: `${this.prefix}${objectName}`,
36507
+ Body: fileBuffer,
36508
+ ContentLength: fileBuffer.length,
36509
+ ContentType: standardHeaders.ContentType || getContentType(filePath),
36510
+ CacheControl: standardHeaders.CacheControl,
36511
+ ContentDisposition: standardHeaders.ContentDisposition,
36512
+ ContentEncoding: standardHeaders.ContentEncoding,
36513
+ ContentLanguage: standardHeaders.ContentLanguage,
36514
+ Expires: standardHeaders.Expires,
36515
+ Metadata: customMetadata
36516
+ });
36517
+ } else {
36518
+ const fileStream = fs.createReadStream(filePath);
36519
+ command = new import_client_s3.PutObjectCommand({
36520
+ Bucket: this.bucketName,
36521
+ Key: `${this.prefix}${objectName}`,
36522
+ Body: fileStream,
36523
+ ContentLength: stat.size,
36524
+ ContentType: standardHeaders.ContentType || getContentType(filePath),
36525
+ CacheControl: standardHeaders.CacheControl,
36526
+ ContentDisposition: standardHeaders.ContentDisposition,
36527
+ ContentEncoding: standardHeaders.ContentEncoding,
36528
+ ContentLanguage: standardHeaders.ContentLanguage,
36529
+ Expires: standardHeaders.Expires,
36530
+ Metadata: customMetadata
36531
+ });
36532
+ }
36508
36533
  const response = await this.client.send(command);
36509
36534
  return {
36510
36535
  etag: response.ETag?.replace(/"/g, "") || "",
@@ -36567,6 +36592,13 @@ class OssBase {
36567
36592
  });
36568
36593
  const response = await this.client.send(command);
36569
36594
  return {
36595
+ standardHeaders: {
36596
+ contentType: response.ContentType,
36597
+ cacheControl: response.CacheControl,
36598
+ contentDisposition: response.ContentDisposition,
36599
+ contentEncoding: response.ContentEncoding,
36600
+ contentLanguage: response.ContentLanguage
36601
+ },
36570
36602
  size: response.ContentLength || 0,
36571
36603
  etag: response.ETag?.replace?.(/"/g, "") || "",
36572
36604
  lastModified: response.LastModified || new Date,
@@ -36574,11 +36606,11 @@ class OssBase {
36574
36606
  versionId: response.VersionId || null
36575
36607
  };
36576
36608
  } catch (e) {
36577
- console.error("statObject error", e);
36578
36609
  const isNotFound = e.name === "NotFound" || e.name === "NoSuchBucket" || e.name === "NoSuchKey" || e.code === "NotFound" || e.code === "NoSuchBucket" || e.code === "NoSuchKey" || e.$metadata?.httpStatusCode === 404;
36579
36610
  if (checkFile && isNotFound) {
36580
36611
  return null;
36581
36612
  }
36613
+ console.error("statObject error", e);
36582
36614
  throw e;
36583
36615
  }
36584
36616
  }
@@ -36659,6 +36691,13 @@ class OssBase {
36659
36691
  });
36660
36692
  }
36661
36693
  }
36694
+ var getStram = (data) => {
36695
+ if (data.Body) {
36696
+ return data.Body;
36697
+ }
36698
+ throw new Error("Object body is empty");
36699
+ };
36662
36700
  export {
36701
+ getStram,
36663
36702
  OssBase
36664
36703
  };
@@ -1,36 +1,40 @@
1
- import * as _aws_sdk_client_s3 from '@aws-sdk/client-s3';
2
1
  import { CopyObjectCommandOutput, S3Client, GetObjectCommandOutput } from '@aws-sdk/client-s3';
3
2
  import { Readable } from 'node:stream';
4
- import { ServerResponse } from 'node:http';
5
- import { ItemBucketMetadata as ItemBucketMetadata$1, Client } from 'minio';
6
3
 
7
4
  type ItemBucketMetadata = Record<string, string>;
8
- type UploadedObjectInfo$1 = {
5
+ type UploadedObjectInfo = {
9
6
  etag: string;
10
7
  lastModified?: Date;
11
8
  size?: number;
12
9
  versionId: string;
13
10
  metadata?: ItemBucketMetadata;
14
11
  };
15
- type StatObjectResult$1 = {
12
+ type StatObjectResult = {
16
13
  size: number;
17
14
  etag: string;
18
15
  lastModified: Date;
19
16
  metaData: ItemBucketMetadata;
20
17
  versionId?: string | null;
18
+ standardHeaders: {
19
+ contentType?: string;
20
+ cacheControl?: string;
21
+ contentDisposition?: string;
22
+ contentEncoding?: string;
23
+ contentLanguage?: string;
24
+ };
21
25
  };
22
- type ListFileObject$1 = {
26
+ type ListFileObject = {
23
27
  name: string;
24
28
  size: number;
25
29
  lastModified: Date;
26
30
  etag: string;
27
31
  };
28
- type ListDirectoryObject$1 = {
32
+ type ListDirectoryObject = {
29
33
  prefix: string;
30
34
  size: number;
31
35
  };
32
- type ListObjectResult$1 = ListFileObject$1 | ListDirectoryObject$1;
33
- interface OssBaseOperation$1 {
36
+ type ListObjectResult = ListFileObject | ListDirectoryObject;
37
+ interface OssBaseOperation {
34
38
  prefix: string;
35
39
  /**
36
40
  * 设置前缀
@@ -64,20 +68,20 @@ interface OssBaseOperation$1 {
64
68
  isStream?: boolean;
65
69
  size?: number;
66
70
  contentType?: string;
67
- }): Promise<UploadedObjectInfo$1>;
71
+ }): Promise<UploadedObjectInfo>;
68
72
  /**
69
73
  * 上传文件
70
74
  * @param objectName 对象名
71
75
  * @param filePath 文件路径
72
76
  * @param metaData 元数据
73
77
  */
74
- fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata): Promise<UploadedObjectInfo$1>;
78
+ fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata): Promise<UploadedObjectInfo>;
75
79
  /**
76
80
  * 获取对象信息
77
81
  * @param objectName 对象名
78
82
  * @param checkFile 是否检查文件存在(不存在返回null而非抛错)
79
83
  */
80
- statObject(objectName: string, checkFile?: boolean): Promise<StatObjectResult$1 | null>;
84
+ statObject(objectName: string, checkFile?: boolean): Promise<StatObjectResult | null>;
81
85
  /**
82
86
  * 删除对象
83
87
  * @param objectName 对象名
@@ -95,7 +99,7 @@ interface OssBaseOperation$1 {
95
99
  startAfter?: string;
96
100
  /** 最大返回数量 */
97
101
  maxKeys?: number;
98
- }): Promise<ListObjectResult$1[]>;
102
+ }): Promise<ListObjectResult[]>;
99
103
  /**
100
104
  * 获取完整的对象名称
101
105
  * @param objectName 对象名
@@ -110,7 +114,7 @@ interface OssBaseOperation$1 {
110
114
  checkObjectHash?(objectName: string, hash: string, meta?: ItemBucketMetadata): Promise<{
111
115
  success: boolean;
112
116
  metaData: ItemBucketMetadata | null;
113
- obj: StatObjectResult$1 | null;
117
+ obj: StatObjectResult | null;
114
118
  equalMeta?: boolean;
115
119
  }>;
116
120
  /**
@@ -132,7 +136,7 @@ interface OssBaseOperation$1 {
132
136
  */
133
137
  replaceObject?(objectName: string, meta: ItemBucketMetadata): Promise<any>;
134
138
  }
135
- interface OssService$1 extends OssBaseOperation$1 {
139
+ interface OssService extends OssBaseOperation {
136
140
  owner: string;
137
141
  }
138
142
 
@@ -152,7 +156,7 @@ type OssBaseOptions<T = {
152
156
  */
153
157
  prefix?: string;
154
158
  } & T;
155
- declare class OssBase implements OssBaseOperation$1 {
159
+ declare class OssBase implements OssBaseOperation {
156
160
  client: S3Client;
157
161
  bucketName: string;
158
162
  prefix: string;
@@ -189,14 +193,14 @@ declare class OssBase implements OssBaseOperation$1 {
189
193
  isStream?: boolean;
190
194
  size?: number;
191
195
  contentType?: string;
192
- }): Promise<UploadedObjectInfo$1>;
196
+ }): Promise<UploadedObjectInfo>;
193
197
  /**
194
198
  * 上传文件
195
199
  * @param objectName 对象名
196
200
  * @param filePath 文件路径
197
201
  * @param metaData 元数据
198
202
  */
199
- fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata): Promise<UploadedObjectInfo$1>;
203
+ fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata): Promise<UploadedObjectInfo>;
200
204
  /**
201
205
  * 删除对象
202
206
  * @param objectName 对象名
@@ -212,13 +216,13 @@ declare class OssBase implements OssBaseOperation$1 {
212
216
  recursive?: boolean;
213
217
  startAfter?: string;
214
218
  maxKeys?: number;
215
- }): Promise<IS_FILE extends true ? ListFileObject$1[] : ListObjectResult$1[]>;
219
+ }): Promise<IS_FILE extends true ? ListFileObject[] : ListObjectResult[]>;
216
220
  /**
217
221
  * 获取对象信息
218
222
  * @param objectName 对象名
219
223
  * @param checkFile 是否检查文件存在(不存在返回null而非抛错)
220
224
  */
221
- statObject(objectName: string, checkFile?: boolean): Promise<StatObjectResult$1 | null>;
225
+ statObject(objectName: string, checkFile?: boolean): Promise<StatObjectResult | null>;
222
226
  /**
223
227
  * 获取完整的对象名称
224
228
  * @param objectName 对象名
@@ -233,7 +237,7 @@ declare class OssBase implements OssBaseOperation$1 {
233
237
  checkObjectHash(objectName: string, hash: string, meta?: ItemBucketMetadata): Promise<{
234
238
  success: boolean;
235
239
  metaData: ItemBucketMetadata | null;
236
- obj: StatObjectResult$1 | null;
240
+ obj: StatObjectResult | null;
237
241
  equalMeta?: boolean;
238
242
  }>;
239
243
  /**
@@ -267,18 +271,18 @@ declare class OssBase implements OssBaseOperation$1 {
267
271
  }): T;
268
272
  }
269
273
 
270
- declare class ConfigOssService extends OssBase implements OssService$1 {
274
+ declare class ConfigOssService extends OssBase implements OssService {
271
275
  owner: string;
272
276
  constructor(opts: OssBaseOptions<{
273
277
  owner: string;
274
278
  }>);
275
- listAllFile(): Promise<ListFileObject$1[]>;
276
- listAll(): Promise<ListObjectResult$1[]>;
279
+ listAllFile(): Promise<ListFileObject[]>;
280
+ listAll(): Promise<ListObjectResult[]>;
277
281
  configMap: Map<string, any>;
278
282
  keys: string[];
279
- getAllConfigJson(): Promise<ListFileObject$1[]>;
283
+ getAllConfigJson(): Promise<ListFileObject[]>;
280
284
  isEndWithJson(string: string): boolean;
281
- putJsonObject(key: string, data: any): Promise<UploadedObjectInfo$1>;
285
+ putJsonObject(key: string, data: any): Promise<UploadedObjectInfo>;
282
286
  getObjectList(objectNameList: string[]): Promise<Map<string, Record<string, any>>>;
283
287
  getList(): Promise<{
284
288
  list: {
@@ -293,25 +297,6 @@ declare class ConfigOssService extends OssBase implements OssService$1 {
293
297
  }>;
294
298
  }
295
299
 
296
- /**
297
- * 过滤 metaData 中的 key, 去除 password, accesskey, secretkey,
298
- * 并返回过滤后的 metaData
299
- * @param metaData
300
- * @returns
301
- */
302
- declare const filterMetaDataKeys: (metaData: Record<string, string>, clearKeys?: string[]) => Record<string, string>;
303
- type SendObjectOptions = {
304
- res: ServerResponse;
305
- client: OssBase;
306
- objectName: string;
307
- isDownload?: boolean;
308
- };
309
- declare const NotFoundFile: (res: ServerResponse, msg?: string, code?: number) => void;
310
- declare const sendObject: ({ res, objectName, client, isDownload }: SendObjectOptions) => Promise<void>;
311
- declare const downloadObject: ({ objectName, client, filePath }: Pick<SendObjectOptions, "objectName" | "client"> & {
312
- filePath: string;
313
- }) => Promise<_aws_sdk_client_s3.GetObjectCommandOutput>;
314
-
315
300
  /**
316
301
  * 计算字符串的md5值
317
302
  * @param str
@@ -341,88 +326,5 @@ declare function extractStandardHeaders(metaData: Record<string, string>): {
341
326
  customMetadata: Record<string, string>;
342
327
  };
343
328
 
344
- type UploadedObjectInfo = {
345
- etag: string;
346
- lastModified?: Date;
347
- size?: number;
348
- versionId: string;
349
- metadata?: ItemBucketMetadata$1;
350
- };
351
- type StatObjectResult = {
352
- size: number;
353
- etag: string;
354
- lastModified: Date;
355
- metaData: ItemBucketMetadata$1;
356
- versionId?: string | null;
357
- };
358
- type ListFileObject = {
359
- name: string;
360
- size: number;
361
- lastModified: Date;
362
- etag: string;
363
- };
364
- type ListDirectoryObject = {
365
- prefix: string;
366
- size: number;
367
- };
368
- type ListObjectResult = ListFileObject | ListDirectoryObject;
369
- interface OssBaseOperation {
370
- prefix: string;
371
- setPrefix(prefix: string): void;
372
- /**
373
- * 获取对象
374
- * @param objectName 对象名
375
- */
376
- getObject(objectName: string): Promise<any>;
377
- /**
378
- * 上传对象
379
- * @param objectName 对象名
380
- * @param data 数据
381
- */
382
- putObject(objectName: string, data: Buffer | string, metaData?: ItemBucketMetadata$1): Promise<UploadedObjectInfo>;
383
- /**
384
- * 上传文件
385
- * @param objectName 对象名
386
- * @param filePath 文件路径
387
- */
388
- fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata$1): Promise<UploadedObjectInfo>;
389
- /**
390
- * 获取对象信息
391
- * @param objectName 对象名
392
- */
393
- statObject(objectName: string): Promise<StatObjectResult>;
394
- /**
395
- * 删除对象
396
- * @param objectName 对象名
397
- */
398
- deleteObject(objectName: string): Promise<any>;
399
- /**
400
- * 列出对象
401
- * @param objectName 对象名
402
- * @param opts 选项
403
- * @param opts.recursive 是否递归
404
- * @param opts.startAfter 开始位置
405
- */
406
- listObjects(objectName: string, opts?: {
407
- /**
408
- * 是否递归
409
- */
410
- recursive?: boolean;
411
- /**
412
- * 开始位置
413
- */
414
- startAfter?: string;
415
- }): Promise<ListObjectResult[]>;
416
- /**
417
- * 复制对象
418
- * @param sourceObject 源对象
419
- * @param targetObject 目标对象
420
- */
421
- copyObject: Client['copyObject'];
422
- }
423
- interface OssService extends OssBaseOperation {
424
- owner: string;
425
- }
426
-
427
- export { ConfigOssService, NotFoundFile, OssBase, downloadObject, extractStandardHeaders, filterMetaDataKeys, getContentType, hash, hashSringify, sendObject, standardHeaderKeys };
428
- export type { ListDirectoryObject, ListFileObject, ListObjectResult, OssBaseOperation, OssBaseOptions, OssService, StandardHeaders, StatObjectResult, UploadedObjectInfo };
329
+ export { ConfigOssService, OssBase, extractStandardHeaders, getContentType, hash, hashSringify, standardHeaderKeys };
330
+ export type { ItemBucketMetadata, ListDirectoryObject, ListFileObject, ListObjectResult, OssBaseOperation, OssBaseOptions, OssService, StandardHeaders, StatObjectResult, UploadedObjectInfo };
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
- throw new Error("Stream upload requires size parameter");
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", "Content-Type", "Cache-Control", "app-source"];
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 command = new import_client_s3.PutObjectCommand({
36500
- Bucket: this.bucketName,
36501
- Key: `${this.prefix}${objectName}`,
36502
- Body: fileStream,
36503
- ContentLength: stat.size,
36504
- ContentType: standardHeaders.ContentType || getContentType(filePath),
36505
- CacheControl: standardHeaders.CacheControl,
36506
- ContentDisposition: standardHeaders.ContentDisposition,
36507
- ContentEncoding: standardHeaders.ContentEncoding,
36508
- ContentLanguage: standardHeaders.ContentLanguage,
36509
- Expires: standardHeaders.Expires,
36510
- Metadata: customMetadata
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, "") || "",
@@ -36571,6 +36596,13 @@ class OssBase {
36571
36596
  });
36572
36597
  const response = await this.client.send(command);
36573
36598
  return {
36599
+ standardHeaders: {
36600
+ contentType: response.ContentType,
36601
+ cacheControl: response.CacheControl,
36602
+ contentDisposition: response.ContentDisposition,
36603
+ contentEncoding: response.ContentEncoding,
36604
+ contentLanguage: response.ContentLanguage
36605
+ },
36574
36606
  size: response.ContentLength || 0,
36575
36607
  etag: response.ETag?.replace?.(/"/g, "") || "",
36576
36608
  lastModified: response.LastModified || new Date,
@@ -36578,11 +36610,11 @@ class OssBase {
36578
36610
  versionId: response.VersionId || null
36579
36611
  };
36580
36612
  } catch (e) {
36581
- console.error("statObject error", e);
36582
36613
  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
36614
  if (checkFile && isNotFound) {
36584
36615
  return null;
36585
36616
  }
36617
+ console.error("statObject error", e);
36586
36618
  throw e;
36587
36619
  }
36588
36620
  }
@@ -36723,69 +36755,12 @@ class ConfigOssService extends OssBase {
36723
36755
  return { list: listKeys, keys, keyEtagMap };
36724
36756
  }
36725
36757
  }
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
36758
  export {
36780
36759
  standardHeaderKeys,
36781
- sendObject,
36782
36760
  hashSringify,
36783
36761
  hash,
36784
36762
  getContentType,
36785
- filterMetaDataKeys,
36786
36763
  extractStandardHeaders,
36787
- downloadObject,
36788
36764
  OssBase,
36789
- NotFoundFile,
36790
36765
  ConfigOssService
36791
36766
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevisual/oss",
3
- "version": "0.0.17",
3
+ "version": "0.0.18",
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
@@ -1 +1,3 @@
1
- export * from './s3/core.ts'
1
+ export * from './s3/core.ts'
2
+
3
+ export * from './s3/type.ts'
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
- putData = data as Readable;
130
+ // Stream 上传:自动读取到 Buffer 以获取 contentLength
131
131
  if (!contentLength) {
132
- throw new Error('Stream upload requires size parameter');
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', 'Content-Type', 'Cache-Control', 'app-source'];
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 command = new PutObjectCommand({
189
- Bucket: this.bucketName,
190
- Key: `${this.prefix}${objectName}`,
191
- Body: fileStream,
192
- ContentLength: stat.size,
193
- ContentType: standardHeaders.ContentType || getContentType(filePath),
194
- CacheControl: standardHeaders.CacheControl,
195
- ContentDisposition: standardHeaders.ContentDisposition,
196
- ContentEncoding: standardHeaders.ContentEncoding,
197
- ContentLanguage: standardHeaders.ContentLanguage,
198
- Expires: standardHeaders.Expires,
199
- Metadata: customMetadata,
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 {
@@ -290,6 +318,13 @@ export class OssBase implements OssBaseOperation {
290
318
  const response = await this.client.send(command);
291
319
 
292
320
  return {
321
+ standardHeaders: {
322
+ contentType: response.ContentType,
323
+ cacheControl: response.CacheControl,
324
+ contentDisposition: response.ContentDisposition,
325
+ contentEncoding: response.ContentEncoding,
326
+ contentLanguage: response.ContentLanguage,
327
+ },
293
328
  size: response.ContentLength || 0,
294
329
  etag: response.ETag?.replace?.(/"/g, '') || '',
295
330
  lastModified: response.LastModified || new Date(),
@@ -297,7 +332,6 @@ export class OssBase implements OssBaseOperation {
297
332
  versionId: response.VersionId || null,
298
333
  };
299
334
  } catch (e: any) {
300
- console.error('statObject error', e);
301
335
  // 检查是否是 404 错误 - 支持多种 S3 兼容存储的错误格式
302
336
  const isNotFound =
303
337
  e.name === 'NotFound' ||
@@ -311,6 +345,7 @@ export class OssBase implements OssBaseOperation {
311
345
  if (checkFile && isNotFound) {
312
346
  return null;
313
347
  }
348
+ console.error('statObject error', e);
314
349
  throw e;
315
350
  }
316
351
  }
@@ -438,4 +473,11 @@ export class OssBase implements OssBaseOperation {
438
473
  ...opts,
439
474
  });
440
475
  }
476
+ }
477
+
478
+ export const getStram = (data: GetObjectCommandOutput) => {
479
+ if (data.Body) {
480
+ return data.Body as Readable;
481
+ }
482
+ throw new Error('Object body is empty');
441
483
  }
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 = {
@@ -6,4 +6,4 @@ export * from '../util/download.ts';
6
6
 
7
7
  export * from '../util/index.ts';
8
8
 
9
- export * from '../core/type.ts';
9
+ export * from '../s3/type.ts';
@@ -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
- * 过滤 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}"`;
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
- 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);
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
- objectStream.pipe(res, { end: true });
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
- 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
- };
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
+ // };
@@ -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
- };
package/src/core/core.ts DELETED
@@ -1,244 +0,0 @@
1
- import { Client, ItemBucketMetadata } from 'minio';
2
- import { ListFileObject, ListObjectResult, OssBaseOperation } from './type.ts';
3
- import { hash } from '../util/hash.ts';
4
- import { copyObject } from './copy-object.ts';
5
- import { getContentType } from '../util/get-content-type.ts';
6
- import { omit } from 'es-toolkit'
7
- export type OssBaseOptions<T = { [key: string]: any }> = {
8
- /**
9
- * 已经初始化好的minio client
10
- */
11
- client: Client;
12
- /**
13
- * 桶名
14
- */
15
- bucketName: string;
16
- /**
17
- * 前缀
18
- */
19
- prefix?: string;
20
- } & T;
21
-
22
- export class OssBase implements OssBaseOperation {
23
- client?: Client;
24
- bucketName: string;
25
- prefix = '';
26
- /**
27
- * 计算字符串或者对象的的md5值
28
- */
29
- hash = hash;
30
- constructor(opts: OssBaseOptions) {
31
- if (!opts.client) {
32
- throw new Error('client is required');
33
- }
34
- this.bucketName = opts.bucketName;
35
- this.client = opts.client;
36
- this.prefix = opts?.prefix ?? '';
37
- }
38
-
39
- setPrefix(prefix: string) {
40
- this.prefix = prefix;
41
- }
42
-
43
- async getObject(objectName: string) {
44
- const bucketName = this.bucketName;
45
- const obj = await this.client.getObject(bucketName, `${this.prefix}${objectName}`);
46
- return obj;
47
- }
48
-
49
- async getJson(objectName: string): Promise<Record<string, any>> {
50
- const obj = await this.getObject(objectName);
51
- return new Promise((resolve, reject) => {
52
- let data = '';
53
- obj.on('data', (chunk) => {
54
- data += chunk;
55
- });
56
- obj.on('end', () => {
57
- try {
58
- const jsonData = JSON.parse(data);
59
- resolve(jsonData);
60
- } catch (error) {
61
- reject(new Error('Failed to parse JSON'));
62
- }
63
- });
64
- obj.on('error', (err) => {
65
- reject(err);
66
- });
67
- });
68
- }
69
- /**
70
- * 上传文件, 当是流的时候,中断之后的etag会变,所以传递的时候不要嵌套async await,例如 busboy 监听文件流内部的时候,不要用check
71
- * @param objectName
72
- * @param data
73
- * @param metaData
74
- * @param options 如果文件本身存在,则复制原有的meta的内容
75
- * @returns
76
- */
77
- async putObject(
78
- objectName: string,
79
- data: Buffer | string | Object,
80
- metaData: ItemBucketMetadata = {},
81
- opts?: { check?: boolean; isStream?: boolean; size?: number },
82
- ) {
83
- let putData: Buffer | string;
84
- let size: number = opts?.size;
85
- const isStream = opts?.isStream;
86
- if (!isStream) {
87
- if (typeof data === 'string') {
88
- putData = data;
89
- size = putData.length;
90
- } else {
91
- putData = JSON.stringify(data);
92
- size = putData.length;
93
- }
94
- } else {
95
- putData = data as any;
96
- // 对于流式上传,如果没有提供 size,会导致多部分上传,ETag 会是 ****-1 格式
97
- // 必须提供准确的 size 才能得到标准的 MD5 格式 ETag
98
- if (!size) {
99
- throw new Error('Stream upload requires size parameter to avoid multipart upload and get standard MD5 ETag');
100
- }
101
- }
102
- if (opts?.check) {
103
- const obj = await this.statObject(objectName, true);
104
- if (obj) {
105
- const omitMeda = ['size', 'content-type', 'cache-control', 'app-source'];
106
- const objMeta = JSON.parse(JSON.stringify(omit(obj.metaData, omitMeda)));
107
- metaData = {
108
- ...objMeta,
109
- ...metaData,
110
- };
111
- }
112
- }
113
-
114
- const bucketName = this.bucketName;
115
- const obj = await this.client.putObject(bucketName, `${this.prefix}${objectName}`, putData, size, metaData);
116
- return obj;
117
- }
118
-
119
- async deleteObject(objectName: string) {
120
- const bucketName = this.bucketName;
121
- const obj = await this.client.removeObject(bucketName, `${this.prefix}${objectName}`);
122
- return obj;
123
- }
124
-
125
- async listObjects<IS_FILE = false>(objectName: string, opts?: { recursive?: boolean; startAfter?: string }) {
126
- const bucketName = this.bucketName;
127
- const prefix = `${this.prefix}${objectName}`;
128
- const res = await new Promise((resolve, reject) => {
129
- let res: any[] = [];
130
- let hasError = false;
131
- this.client
132
- .listObjectsV2(bucketName, prefix, opts?.recursive ?? false, opts?.startAfter)
133
- .on('data', (data) => {
134
- res.push(data);
135
- })
136
- .on('error', (err) => {
137
- console.error('minio error', prefix, err);
138
- hasError = true;
139
- })
140
- .on('end', () => {
141
- if (hasError) {
142
- reject();
143
- return;
144
- } else {
145
- resolve(res);
146
- }
147
- });
148
- });
149
- return res as IS_FILE extends true ? ListFileObject[] : ListObjectResult[];
150
- }
151
-
152
- async fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata) {
153
- const bucketName = this.bucketName;
154
- const obj = await this.client.fPutObject(bucketName, `${this.prefix}${objectName}`, filePath, metaData);
155
- return obj as any;
156
- }
157
- /**
158
- * 获取完整的对象名称
159
- * @param objectName
160
- * @returns
161
- */
162
- async getObjectName(objectName: string) {
163
- return `${this.prefix}${objectName}`;
164
- }
165
- async statObject(objectName: string, checkFile = true) {
166
- const bucketName = this.bucketName;
167
- try {
168
- const obj = await this.client.statObject(bucketName, `${this.prefix}${objectName}`);
169
- return obj;
170
- } catch (e) {
171
- if (e.code === 'NotFound') {
172
- return null;
173
- }
174
- throw e;
175
- }
176
- }
177
- /**
178
- * 检查文件hash是否一致
179
- * @param objectName
180
- * @param hash
181
- * @returns
182
- */
183
- async checkObjectHash(
184
- objectName: string,
185
- hash: string,
186
- meta?: ItemBucketMetadata,
187
- ): Promise<{ success: boolean; metaData: ItemBucketMetadata | null; obj: any; equalMeta?: boolean }> {
188
- const obj = await this.statObject(`${this.prefix}${objectName}`, true);
189
- if (!obj) {
190
- return { success: false, metaData: null, obj: null, equalMeta: false };
191
- }
192
- let metaData: ItemBucketMetadata = {};
193
- const omitMeda = ['content-type', 'cache-control', 'app-source'];
194
- const objMeta = omit(obj.metaData, omitMeda);
195
- metaData = {
196
- ...objMeta,
197
- };
198
- let equalMeta = false;
199
- if (meta) {
200
- equalMeta = JSON.stringify(metaData) === JSON.stringify(meta);
201
- }
202
- return { success: obj.etag === hash, metaData, obj, equalMeta };
203
- }
204
- getMetadata(pathname: string, meta: ItemBucketMetadata = { 'app-source': 'user-app' }) {
205
- const isHtml = pathname.endsWith('.html');
206
- if (isHtml) {
207
- meta = {
208
- ...meta,
209
- 'content-type': 'text/html; charset=utf-8',
210
- 'cache-control': 'no-cache',
211
- };
212
- } else {
213
- meta = {
214
- ...meta,
215
- 'content-type': getContentType(pathname),
216
- 'cache-control': 'max-age=31536000, immutable',
217
- };
218
- }
219
- return meta;
220
- }
221
-
222
- async copyObject(sourceObject: any, targetObject: any) {
223
- const bucketName = this.bucketName;
224
- const obj = await this.client.copyObject(bucketName, sourceObject, targetObject);
225
- return obj;
226
- }
227
- async replaceObject(objectName: string, meta: { [key: string]: string }) {
228
- const { bucketName, client } = this;
229
- return copyObject({ bucketName, client, objectName: `${this.prefix}${objectName}`, newMetadata: meta });
230
- }
231
- static create<T extends OssBase, U>(this: new (opts: OssBaseOptions<U>) => T, opts: OssBaseOptions<U>): T {
232
- return new this(opts);
233
- }
234
-
235
- static fromBase<T extends OssBase, U>(this: new (opts: OssBaseOptions<U>) => T, createOpts: { oss: OssBase; opts: Partial<OssBaseOptions<U>> }): T {
236
- const base = createOpts.oss;
237
- const opts = createOpts.opts as any;
238
- return new this({
239
- client: base.client,
240
- bucketName: base.bucketName,
241
- ...opts,
242
- });
243
- }
244
- }
package/src/core/type.ts DELETED
@@ -1,87 +0,0 @@
1
- import { ItemBucketMetadata, Client } from 'minio';
2
- export type UploadedObjectInfo = {
3
- etag: string;
4
- lastModified?: Date;
5
- size?: number;
6
- versionId: string;
7
- metadata?: ItemBucketMetadata;
8
- };
9
- export type StatObjectResult = {
10
- size: number;
11
- etag: string;
12
- lastModified: Date;
13
- metaData: ItemBucketMetadata;
14
- versionId?: string | null;
15
- };
16
- export type ListFileObject = {
17
- name: string;
18
- size: number;
19
- lastModified: Date;
20
- etag: string;
21
- };
22
- export type ListDirectoryObject = {
23
- prefix: string;
24
- size: number;
25
- };
26
- export type ListObjectResult = ListFileObject | ListDirectoryObject;
27
- export interface OssBaseOperation {
28
- prefix: string;
29
- setPrefix(prefix: string): void;
30
- /**
31
- * 获取对象
32
- * @param objectName 对象名
33
- */
34
- getObject(objectName: string): Promise<any>;
35
- /**
36
- * 上传对象
37
- * @param objectName 对象名
38
- * @param data 数据
39
- */
40
- putObject(objectName: string, data: Buffer | string, metaData?: ItemBucketMetadata): Promise<UploadedObjectInfo>;
41
- /**
42
- * 上传文件
43
- * @param objectName 对象名
44
- * @param filePath 文件路径
45
- */
46
- fPutObject(objectName: string, filePath: string, metaData?: ItemBucketMetadata): Promise<UploadedObjectInfo>;
47
- /**
48
- * 获取对象信息
49
- * @param objectName 对象名
50
- */
51
- statObject(objectName: string): Promise<StatObjectResult>;
52
- /**
53
- * 删除对象
54
- * @param objectName 对象名
55
- */
56
- deleteObject(objectName: string): Promise<any>;
57
- /**
58
- * 列出对象
59
- * @param objectName 对象名
60
- * @param opts 选项
61
- * @param opts.recursive 是否递归
62
- * @param opts.startAfter 开始位置
63
- */
64
- listObjects(
65
- objectName: string,
66
- opts?: {
67
- /**
68
- * 是否递归
69
- */
70
- recursive?: boolean;
71
- /**
72
- * 开始位置
73
- */
74
- startAfter?: string;
75
- },
76
- ): Promise<ListObjectResult[]>;
77
- /**
78
- * 复制对象
79
- * @param sourceObject 源对象
80
- * @param targetObject 目标对象
81
- */
82
- copyObject: Client['copyObject'];
83
- }
84
-
85
- export interface OssService extends OssBaseOperation {
86
- owner: string;
87
- }