@maiyunnet/kebab 9.20.0 → 9.20.1

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/doc/kebab-rag.md CHANGED
@@ -1589,7 +1589,7 @@ index/variables/VER.md
1589
1589
 
1590
1590
  # Variable: VER
1591
1591
 
1592
- > `const` **VER**: `"9.20.0"` = `'9.20.0'`
1592
+ > `const` **VER**: `"9.20.1"` = `'9.20.1'`
1593
1593
 
1594
1594
  Defined in: [index.ts:10](https://github.com/maiyunnet/kebab/blob/master/index.ts#L10)
1595
1595
 
@@ -122018,7 +122018,7 @@ Defined in: [lib/s3.ts:88](https://github.com/maiyunnet/kebab/blob/master/lib/s3
122018
122018
 
122019
122019
  > **deleteObject**(`key`, `bucket?`): `Promise`\<`boolean`\>
122020
122020
 
122021
- Defined in: [lib/s3.ts:279](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L279)
122021
+ Defined in: [lib/s3.ts:367](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L367)
122022
122022
 
122023
122023
  删除对象
122024
122024
 
@@ -122046,7 +122046,7 @@ bucket 名
122046
122046
 
122047
122047
  > **deleteObjects**(`keys`, `bucket?`): `Promise`\<`boolean`\>
122048
122048
 
122049
- Defined in: [lib/s3.ts:299](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L299)
122049
+ Defined in: [lib/s3.ts:387](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L387)
122050
122050
 
122051
122051
  批量删除对象
122052
122052
 
@@ -122074,7 +122074,7 @@ bucket 名
122074
122074
 
122075
122075
  > **destroy**(): `void`
122076
122076
 
122077
- Defined in: [lib/s3.ts:341](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L341)
122077
+ Defined in: [lib/s3.ts:429](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L429)
122078
122078
 
122079
122079
  销毁连接,释放资源
122080
122080
  一般会自动垃圾回收,但高频接口也可主动调用
@@ -122089,7 +122089,7 @@ Defined in: [lib/s3.ts:341](https://github.com/maiyunnet/kebab/blob/master/lib/s
122089
122089
 
122090
122090
  > **getObject**(`key`, `bucket?`): `Promise`\<`false` \| `Readable` & `SdkStreamMixin` \| `Blob` & `SdkStreamMixin` \| `ReadableStream`\<`any`\> & `SdkStreamMixin` \| `undefined`\>
122091
122091
 
122092
- Defined in: [lib/s3.ts:235](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L235)
122092
+ Defined in: [lib/s3.ts:323](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L323)
122093
122093
 
122094
122094
  获取对象流,可通过流获取 buffer 或 text
122095
122095
 
@@ -122117,7 +122117,7 @@ bucket 名
122117
122117
 
122118
122118
  > **getObjectBuffer**(`key`, `bucket?`): `Promise`\<`false` \| `Buffer`\<`ArrayBufferLike`\>\>
122119
122119
 
122120
- Defined in: [lib/s3.ts:256](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L256)
122120
+ Defined in: [lib/s3.ts:344](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L344)
122121
122121
 
122122
122122
  获取对象 Buffer
122123
122123
 
@@ -122147,7 +122147,7 @@ bucket 名
122147
122147
 
122148
122148
  > **headObject**(`key`, `bucket?`): `Promise`\<`false` \| `HeadObjectCommandOutput`\>
122149
122149
 
122150
- Defined in: [lib/s3.ts:321](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L321)
122150
+ Defined in: [lib/s3.ts:409](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L409)
122151
122151
 
122152
122152
  检测对象是否存在
122153
122153
 
@@ -122175,9 +122175,9 @@ bucket 名
122175
122175
 
122176
122176
  > **putObject**(`key`, `content`, `length?`, `bucket?`): `Promise`\<`false` \| `CompleteMultipartUploadCommandOutput`\>
122177
122177
 
122178
- Defined in: [lib/s3.ts:139](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L139)
122178
+ Defined in: [lib/s3.ts:140](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L140)
122179
122179
 
122180
- 上传对象(可传流且也可无需设置 length) --
122180
+ 上传对象,流内容可省略 length
122181
122181
 
122182
122182
  #### Parameters
122183
122183
 
@@ -122195,9 +122195,9 @@ Defined in: [lib/s3.ts:139](https://github.com/maiyunnet/kebab/blob/master/lib/s
122195
122195
 
122196
122196
  ##### length?
122197
122197
 
122198
- `number` \| \{ `bucket?`: `string`; `disposition?`: `string`; `length?`: `number`; `type?`: `string`; \}
122198
+ `number` \| \{ `bucket?`: `string`; `disposition?`: `string`; `length?`: `number`; `signal?`: `AbortSignal`; `type?`: `string`; \}
122199
122199
 
122200
- 设置 contentLength,如果是流模式则需要设置此项,也可以设置为对象参数
122200
+ contentLength 或上传选项
122201
122201
 
122202
122202
  `number`
122203
122203
 
@@ -122205,22 +122205,36 @@ Defined in: [lib/s3.ts:139](https://github.com/maiyunnet/kebab/blob/master/lib/s
122205
122205
 
122206
122206
  ###### Type Literal
122207
122207
 
122208
- \{ `bucket?`: `string`; `disposition?`: `string`; `length?`: `number`; `type?`: `string`; \}
122208
+ \{ `bucket?`: `string`; `disposition?`: `string`; `length?`: `number`; `signal?`: `AbortSignal`; `type?`: `string`; \}
122209
122209
 
122210
- 设置 contentLength,如果是流模式则需要设置此项,也可以设置为对象参数
122210
+ contentLength 或上传选项
122211
122211
 
122212
122212
  ###### bucket?
122213
122213
 
122214
122214
  `string`
122215
122215
 
122216
+ bucket 名,省略时使用预定义 bucket
122217
+
122216
122218
  ###### disposition?
122217
122219
 
122218
122220
  `string`
122219
122221
 
122222
+ content-disposition,如 attachment
122223
+
122220
122224
  ###### length?
122221
122225
 
122222
122226
  `number`
122223
122227
 
122228
+ contentLength,流内容可省略
122229
+
122230
+ ###### signal?
122231
+
122232
+ `AbortSignal`
122233
+
122234
+ 取消上传请求并销毁输入流,等待已知的未完成分片清理后返回 false
122235
+ 清理使用独立的 30 秒超时信号,失败时记录错误;不传则沿用原上传流程
122236
+ 不删除同名对象,上传完成与取消同时发生时,由调用方清理独立任务文件
122237
+
122224
122238
  ###### type?
122225
122239
 
122226
122240
  `string`
@@ -122237,13 +122251,15 @@ bucket 名
122237
122251
 
122238
122252
  `Promise`\<`false` \| `CompleteMultipartUploadCommandOutput`\>
122239
122253
 
122254
+ 上传结果,失败或取消返回 false;传入 signal 时,返回前会尝试清理已知的未完成分片
122255
+
122240
122256
  ***
122241
122257
 
122242
122258
  ### putObjects()
122243
122259
 
122244
122260
  > **putObjects**(`items`, `options?`): `Promise`\<[`IPutObjectsItemResult`](../interfaces/IPutObjectsItemResult.md)[]\>
122245
122261
 
122246
- Defined in: [lib/s3.ts:184](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L184)
122262
+ Defined in: [lib/s3.ts:272](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L272)
122247
122263
 
122248
122264
  批量上传对象,并发控制,单次失败不影响其他项
122249
122265
 
@@ -122347,7 +122363,7 @@ lib/s3/functions/get.md
122347
122363
 
122348
122364
  > **get**(`ctr`, `opt`): [`S3`](../classes/S3.md)
122349
122365
 
122350
- Defined in: [lib/s3.ts:351](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L351)
122366
+ Defined in: [lib/s3.ts:439](https://github.com/maiyunnet/kebab/blob/master/lib/s3.ts#L439)
122351
122367
 
122352
122368
  创建一个对象存储对象
122353
122369
 
package/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * --- 本文件用来定义每个目录实体地址的常量 ---
6
6
  */
7
7
  /** --- 当前系统版本号 --- */
8
- export declare const VER = "9.20.0";
8
+ export declare const VER = "9.20.1";
9
9
  /** --- 框架根目录,以 / 结尾 --- */
10
10
  export declare const ROOT_PATH: string;
11
11
  /** --- 框架的 LIB,以 / 结尾 --- */
package/index.js CHANGED
@@ -6,7 +6,7 @@
6
6
  * --- 本文件用来定义每个目录实体地址的常量 ---
7
7
  */
8
8
  /** --- 当前系统版本号 --- */
9
- export const VER = '9.20.0';
9
+ export const VER = '9.20.1';
10
10
  // --- 服务端用的路径 ---
11
11
  const imu = decodeURIComponent(import.meta.url).replace('file://', '').replace(/^\/(\w:)/, '$1');
12
12
  /** --- /xxx/xxx --- */
package/lib/s3.d.ts CHANGED
@@ -3,8 +3,8 @@
3
3
  * Date: 2024-2-18 18:32:45
4
4
  * Last: 2024-2-18 18:32:47, 2024-3-16 16:42:27, 2024-5-31 21:36:26, 2024-7-8 00:28:42, 2024-7-19 11:32:43, 2025-6-10 21:45:34, 2025-12-5 23:42:59
5
5
  */
6
- import * as s3 from '@aws-sdk/client-s3';
7
6
  import * as stream from 'stream';
7
+ import * as s3 from '@aws-sdk/client-s3';
8
8
  import * as sCtr from '#kebab/sys/ctr.js';
9
9
  /**
10
10
  * --- s3 文档:https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/ ---
@@ -76,18 +76,28 @@ export declare class S3 {
76
76
  */
77
77
  setBucket(bucket: string): void;
78
78
  /**
79
- * --- 上传对象(可传流且也可无需设置 length) --
79
+ * --- 上传对象,流内容可省略 length ---
80
80
  * @param key 对象路径
81
81
  * @param content 内容
82
- * @param length 设置 contentLength,如果是流模式则需要设置此项,也可以设置为对象参数
82
+ * @param length contentLength 或上传选项
83
83
  * @param bucket bucket 名
84
+ * @returns 上传结果,失败或取消返回 false;传入 signal 时,返回前会尝试清理已知的未完成分片
84
85
  */
85
86
  putObject(key: string, content: string | Buffer | stream.Readable, length?: number | {
87
+ /** --- contentLength,流内容可省略 --- */
86
88
  'length'?: number;
87
89
  /** --- content-type,如 application/javascript --- */
88
90
  'type'?: string;
91
+ /** --- content-disposition,如 attachment --- */
89
92
  'disposition'?: string;
93
+ /** --- bucket 名,省略时使用预定义 bucket --- */
90
94
  'bucket'?: string;
95
+ /**
96
+ * --- 取消上传请求并销毁输入流,等待已知的未完成分片清理后返回 false ---
97
+ * --- 清理使用独立的 30 秒超时信号,失败时记录错误;不传则沿用原上传流程 ---
98
+ * --- 不删除同名对象,上传完成与取消同时发生时,由调用方清理独立任务文件 ---
99
+ */
100
+ 'signal'?: AbortSignal;
91
101
  }, bucket?: string): Promise<s3.CompleteMultipartUploadCommandOutput | false>;
92
102
  /**
93
103
  * --- 批量上传对象,并发控制,单次失败不影响其他项 ---
package/lib/s3.js CHANGED
@@ -4,6 +4,7 @@
4
4
  * Last: 2024-2-18 18:32:47, 2024-3-16 16:42:27, 2024-5-31 21:36:26, 2024-7-8 00:28:42, 2024-7-19 11:32:43, 2025-6-10 21:45:34, 2025-12-5 23:42:59
5
5
  */
6
6
  // --- 库和定义 ---
7
+ import * as stream from 'stream';
7
8
  import * as s3 from '@aws-sdk/client-s3';
8
9
  import * as ls from '@aws-sdk/lib-storage';
9
10
  import * as lCore from '#kebab/lib/core.js';
@@ -67,28 +68,79 @@ export class S3 {
67
68
  this._bucket = bucket;
68
69
  }
69
70
  /**
70
- * --- 上传对象(可传流且也可无需设置 length) --
71
+ * --- 上传对象,流内容可省略 length ---
71
72
  * @param key 对象路径
72
73
  * @param content 内容
73
- * @param length 设置 contentLength,如果是流模式则需要设置此项,也可以设置为对象参数
74
+ * @param length contentLength 或上传选项
74
75
  * @param bucket bucket 名
76
+ * @returns 上传结果,失败或取消返回 false;传入 signal 时,返回前会尝试清理已知的未完成分片
75
77
  */
76
78
  async putObject(key, content, length, bucket) {
79
+ /** --- 对象内容类型 --- */
80
+ let type;
81
+ /** --- 对象下载方式 --- */
82
+ let disposition;
83
+ /** --- 调用方传入的取消信号,仅作用于本次上传 --- */
84
+ let signal;
85
+ if (typeof length !== 'number') {
86
+ type = length?.type;
87
+ disposition = length?.disposition;
88
+ bucket = length?.bucket;
89
+ signal = length?.signal;
90
+ length = length?.length;
91
+ }
92
+ bucket ??= this._bucket;
93
+ /** --- 输入流,传入 signal 时在取消或失败后停止读取;字符串与 Buffer 无需释放 --- */
94
+ const source = content instanceof stream.Readable ? content : undefined;
95
+ /**
96
+ * --- 停止流读取,避免取消时仍阻塞在等待下一块数据的阶段 ---
97
+ * @returns 无返回值
98
+ */
99
+ const onAbort = () => {
100
+ source?.destroy();
101
+ };
102
+ /** --- 尚未确认合并或清理完成的分片上传 ID,用于失败后的兜底清理 --- */
103
+ let uploadId;
104
+ /** --- 是否取得有效上传结果,失败时需要释放输入流 --- */
105
+ let success = false;
106
+ /** --- 分片清理请求的超时毫秒数,避免清理阶段无限等待 --- */
107
+ const cleanupTimeout = 30000;
77
108
  try {
78
- /** --- content type --- */
79
- let type = undefined;
80
- /** --- content disposition --- */
81
- let disposition = undefined;
82
- if (typeof length !== 'number') {
83
- type = length?.type;
84
- disposition = length?.disposition;
85
- bucket = length?.bucket;
86
- length = length?.length;
109
+ if (signal?.aborted) {
110
+ return false;
87
111
  }
112
+ signal?.addEventListener('abort', onAbort, { 'once': true });
113
+ /** --- 本次上传的客户端入口,共享连接配置但不修改共享客户端的 send --- */
114
+ const client = signal ? Object.create(this._link) : this._link;
115
+ if (signal) {
116
+ /**
117
+ * --- 为上传命令传递取消信号,并跟踪未完成的分片上传 ---
118
+ * --- Upload.abort() 会提前结束 done(),且不会取消正在发送的请求 ---
119
+ * --- 逐条命令传入信号,让 done() 等待分片任务和清理结束 ---
120
+ * @param command SDK 上传或分片清理命令;Upload 仅使用 send 的 Promise 调用形式
121
+ * @returns SDK 命令结果
122
+ */
123
+ client.send = (async (command) => {
124
+ /** --- 清理请求必须使用独立信号,避免随上传请求一起取消 --- */
125
+ const isCleanup = command instanceof s3.AbortMultipartUploadCommand;
126
+ /** --- 命令成功后再更新分片 ID,失败时保留 ID 供 finally 重试清理 --- */
127
+ const res = await this._link.send(command, {
128
+ 'abortSignal': isCleanup ? AbortSignal.timeout(cleanupTimeout) : signal,
129
+ });
130
+ if (command instanceof s3.CreateMultipartUploadCommand) {
131
+ uploadId = res.UploadId;
132
+ }
133
+ else if (isCleanup || (command instanceof s3.CompleteMultipartUploadCommand)) {
134
+ uploadId = undefined;
135
+ }
136
+ return res;
137
+ });
138
+ }
139
+ /** --- SDK 上传任务,继续由 SDK 管理分片和并发 --- */
88
140
  const upload = new ls.Upload({
89
- 'client': this._link,
141
+ 'client': client,
90
142
  'params': {
91
- 'Bucket': bucket ?? this._bucket,
143
+ 'Bucket': bucket,
92
144
  'Key': key,
93
145
  'Body': content,
94
146
  'ContentLength': length,
@@ -96,13 +148,40 @@ export class S3 {
96
148
  'ContentDisposition': disposition,
97
149
  }
98
150
  });
151
+ /** --- 上传结果;取消与上传完成同时发生时仍向调用方返回 false --- */
99
152
  const res = await upload.done();
100
- return (res.Location && res.Bucket && res.Key) ? res : false;
153
+ if (signal?.aborted || !res.Location || !res.Bucket || !res.Key) {
154
+ return false;
155
+ }
156
+ success = true;
157
+ return res;
101
158
  }
102
159
  catch (e) {
103
- lCore.log(this._ctr, '[LIB][S3][putObject] ' + lText.stringifyJson(e.message ?? '').slice(1, -1).replace(/"/g, '""'), '-error');
160
+ if (!signal?.aborted) {
161
+ lCore.log(this._ctr, `[LIB][S3][putObject] ${lText.stringifyError(e)}`, '-error');
162
+ }
104
163
  return false;
105
164
  }
165
+ finally {
166
+ signal?.removeEventListener('abort', onAbort);
167
+ if (signal && !success) {
168
+ source?.destroy();
169
+ }
170
+ // --- SDK 的合并失败分支不会清理分片;SDK 清理失败时也保留 ID,在这里兜底 ---
171
+ // --- 只清理本次未完成的分片,不删除可能已存在的同名对象 ---
172
+ if (uploadId) {
173
+ try {
174
+ await this._link.send(new s3.AbortMultipartUploadCommand({
175
+ 'Bucket': bucket,
176
+ 'Key': key,
177
+ 'UploadId': uploadId,
178
+ }), { 'abortSignal': AbortSignal.timeout(cleanupTimeout) });
179
+ }
180
+ catch (e) {
181
+ lCore.log(this._ctr, `[LIB][S3][putObject][cleanup] ${lText.stringifyError(e)}`, '-error');
182
+ }
183
+ }
184
+ }
106
185
  }
107
186
  /**
108
187
  * --- 批量上传对象,并发控制,单次失败不影响其他项 ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maiyunnet/kebab",
3
- "version": "9.20.0",
3
+ "version": "9.20.1",
4
4
  "description": "Simple, easy-to-use, and fully-featured Node.js framework that is ready-to-use out of the box.",
5
5
  "type": "module",
6
6
  "keywords": [