@kevisual/oss 0.0.16 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kevisual/oss",
3
- "version": "0.0.16",
3
+ "version": "0.0.18",
4
4
  "main": "dist/index.js",
5
5
  "scripts": {
6
6
  "build": "bun run bun.config.ts"
@@ -14,13 +14,13 @@
14
14
  "license": "MIT",
15
15
  "type": "module",
16
16
  "devDependencies": {
17
- "@types/bun": "^1.3.5",
18
- "@types/node": "^25.0.3",
17
+ "@aws-sdk/client-s3": "^3.978.0",
18
+ "@kevisual/use-config": "^1.0.28",
19
+ "@types/bun": "^1.3.8",
20
+ "@types/node": "^25.1.0",
19
21
  "bun-plugin-dts": "^0.3.0",
20
22
  "dotenv": "^17.2.3",
21
- "minio": "^8.0.6",
22
- "@aws-sdk/client-s3": "^3.0.0",
23
- "es-toolkit": "^1.43.0",
23
+ "es-toolkit": "^1.44.0",
24
24
  "fast-glob": "^3.3.3"
25
25
  },
26
26
  "exports": {
@@ -28,18 +28,13 @@
28
28
  "import": "./dist/index.js",
29
29
  "types": "./dist/index.d.ts"
30
30
  },
31
- "./config": {
32
- "import": "./dist/services/config.js",
33
- "types": "./dist/services/config.d.ts"
34
- },
35
31
  "./services": {
36
- "import": "./dist/services/index.js",
37
- "types": "./dist/services/index.d.ts"
32
+ "import": "./dist/services.js",
33
+ "types": "./dist/services.d.ts"
38
34
  },
39
35
  "./s3.ts": "./src/s3/core.ts"
40
36
  },
41
37
  "publishConfig": {
42
38
  "access": "public"
43
- },
44
- "dependencies": {}
39
+ }
45
40
  }
package/src/index.ts CHANGED
@@ -1,244 +1,3 @@
1
- import { Client, ItemBucketMetadata } from 'minio';
2
- import { ListFileObject, ListObjectResult, OssBaseOperation } from './core/type.ts';
3
- import { hash } from './util/hash.ts';
4
- import { copyObject } from './core/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;
1
+ export * from './s3/core.ts'
21
2
 
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
- }
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
 
@@ -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 {
@@ -217,6 +245,11 @@ export class OssBase implements OssBaseOperation {
217
245
  });
218
246
  await this.client.send(command);
219
247
  }
248
+ async deleteObjects(objectNameList: string[]): Promise<void> {
249
+ for (const objectName of objectNameList) {
250
+ await this.deleteObject(objectName);
251
+ }
252
+ }
220
253
 
221
254
  /**
222
255
  * 列出对象
@@ -285,16 +318,34 @@ export class OssBase implements OssBaseOperation {
285
318
  const response = await this.client.send(command);
286
319
 
287
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
+ },
288
328
  size: response.ContentLength || 0,
289
- etag: response.ETag?.replace(/"/g, '') || '',
329
+ etag: response.ETag?.replace?.(/"/g, '') || '',
290
330
  lastModified: response.LastModified || new Date(),
291
331
  metaData: (response.Metadata as ItemBucketMetadata) || {},
292
332
  versionId: response.VersionId || null,
293
333
  };
294
334
  } catch (e: any) {
295
- if (checkFile && (e.name === 'NotFound' || e.$metadata?.httpStatusCode === 404)) {
335
+ // 检查是否是 404 错误 - 支持多种 S3 兼容存储的错误格式
336
+ const isNotFound =
337
+ e.name === 'NotFound' ||
338
+ e.name === 'NoSuchBucket' ||
339
+ e.name === 'NoSuchKey' ||
340
+ e.code === 'NotFound' ||
341
+ e.code === 'NoSuchBucket' ||
342
+ e.code === 'NoSuchKey' ||
343
+ e.$metadata?.httpStatusCode === 404;
344
+
345
+ if (checkFile && isNotFound) {
296
346
  return null;
297
347
  }
348
+ console.error('statObject error', e);
298
349
  throw e;
299
350
  }
300
351
  }
@@ -322,7 +373,7 @@ export class OssBase implements OssBaseOperation {
322
373
  if (!obj) {
323
374
  return { success: false, metaData: null, obj: null, equalMeta: false };
324
375
  }
325
- const omitMeta = ['content-type', 'cache-control', 'app-source'];
376
+ const omitMeta = ['Content-Type', 'Cache-Control', 'app-source'];
326
377
  const metaData = omit(obj.metaData, omitMeta);
327
378
  let equalMeta = false;
328
379
  if (meta) {
@@ -341,14 +392,14 @@ export class OssBase implements OssBaseOperation {
341
392
  if (isHtml) {
342
393
  meta = {
343
394
  ...meta,
344
- 'content-type': 'text/html; charset=utf-8',
345
- 'cache-control': 'no-cache',
395
+ 'Content-Type': 'text/html; charset=utf-8',
396
+ 'Cache-Control': 'no-cache',
346
397
  };
347
398
  } else {
348
399
  meta = {
349
400
  ...meta,
350
- 'content-type': getContentType(pathname),
351
- 'cache-control': 'max-age=31536000, immutable',
401
+ 'Content-Type': getContentType(pathname),
402
+ 'Cache-Control': 'max-age=31536000, immutable',
352
403
  };
353
404
  }
354
405
  return meta;
@@ -422,4 +473,11 @@ export class OssBase implements OssBaseOperation {
422
473
  ...opts,
423
474
  });
424
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');
425
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 = {
@@ -1,5 +1,5 @@
1
- import { OssBase, OssBaseOptions } from '../index.ts';
2
- import { OssService } from '../core/type.ts';
1
+ import { OssBase, OssBaseOptions } from '../s3/core.ts';
2
+ import { OssService } from '../s3/type.ts';
3
3
  import * as util from '../util/index.ts';
4
4
 
5
5
  export class ConfigOssService extends OssBase implements OssService {
@@ -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';
@@ -6,7 +6,7 @@ import { S3Client, ListObjectsV2Command, GetBucketMetadataConfigurationCommand,
6
6
  export const s3Client = new S3Client({
7
7
  credentials: {
8
8
  accessKeyId: process.env.S3_ACCESS_KEY_ID || '',
9
- secretAccessKey: process.env.S3_SECRET_ACCESS_KEY || '',
9
+ secretAccessKey: process.env.S3_ACCESS_KEY_SECRET || '',
10
10
  },
11
11
  region: process.env.S3_REGION,
12
12
  endpoint: 'https://tos-s3-cn-shanghai.volces.com',
@@ -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
+ // };