fsd-oss 0.14.0 → 0.15.0

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2023 郑州渺漠信息科技有限公司
3
+ Copyright (c) 2026 Liang Xingchen https://github.com/liangxingchen
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,39 +1,66 @@
1
1
  # fsd-oss
2
2
 
3
- FSD OSS文件读写适配器。
3
+ FSD 阿里云 OSS 文件存储适配器 - 提供对阿里云 OSS 对象存储的读写访问。
4
4
 
5
- ```js
6
- const FSD = require('fsd');
7
- const OSSAdapter = require('fsd-oss');
5
+ [![npm version](https://badge.fury.io/js/fsd-oss.svg)](https://www.npmjs.com/package/fsd-oss)
8
6
 
9
- const adapter = new OSSAdapter(config);
10
- const fsd = FSD({ adapter: adapter });
7
+ ## 概述
11
8
 
12
- let file = fsd('/test.txt');
9
+ `fsd-oss` `fsd` 核心库的阿里云 OSS 适配器,提供完整的对象存储操作能力。
13
10
 
14
- let content = await fsd.read();
11
+ ### 核心特性
15
12
 
13
+ - ✅ 完整的文件 CRUD 操作
14
+ - ✅ 分段上传(大文件优化)
15
+ - ✅ STS 角色扮演(边缘上传)
16
+ - ✅ 上传回调通知
17
+ - ✅ 自定义 OSS 客户端
18
+ - ✅ 缩略图支持
19
+
20
+ ## 安装
21
+
22
+ ```bash
23
+ npm install fsd-oss
24
+ ```
25
+
26
+ ## 配置
27
+
28
+ ```typescript
29
+ import FSD from 'fsd';
30
+ import OSSAdapter from 'fsd-oss';
31
+
32
+ // 创建适配器
33
+ const adapter = new OSSAdapter({
34
+ accessKeyId: 'your-access-key-id', // 必需:OSS 访问凭证
35
+ accessKeySecret: 'your-access-key-secret', // 必需:OSS 访问凭证
36
+ region: 'oss-cn-hangzhou', // 必需:OSS 区域
37
+ bucket: 'your-bucket-name' // 可选:Bucket 名称
38
+ });
39
+
40
+ // 创建 FSD 实例
41
+ const fsd = FSD({ adapter });
16
42
  ```
17
43
 
18
- FSD 文档: https://github.com/liangxingchen/fsd
44
+ ### 配置选项说明
19
45
 
20
- 适配器初始化选项:
46
+ | 选项 | 类型 | 必需 | 默认值 | 说明 |
47
+ |------|------|------|--------|------|
48
+ | `accessKeyId` | string | 是 | - | OSS 访问凭证 |
49
+ | `accessKeySecret` | string | 是 | - | OSS 访问凭证 |
50
+ | `region` | string | 是 | - | OSS 区域(如 oss-cn-hangzhou) |
51
+ | `bucket` | string | 否 | - | Bucket 名称(也可在 path 中指定) |
52
+ | `root` | string | 否 | '/' | 存储根路径 |
53
+ | `urlPrefix` | string | 否 | - | URL 前缀,用于生成访问链接 |
54
+ | `publicRead` | boolean | 否 | false | 是否公共读,控制生成的 URL 是否需要签名 |
55
+ | `internal` | boolean | 否 | false | 是否使用内网访问,与应用在同一区域时更快且免费 |
56
+ | `secure` | boolean | 否 | true | 是否使用 HTTPS |
57
+ | `timeout` | number | 否 | - | 请求超时时间(毫秒) |
58
+ | `accountId` | string | 否 | - | STS 角色扮演:阿里云账号 ID |
59
+ | `roleName` | string | 否 | - | STS 角色扮演:角色名称 |
60
+ | `callbackUrl` | string | 否 | - | 上传回调 URL,上传完成后 OSS 会调用此接口 |
61
+ | `thumbs` | object | 否 | - | 缩略图配置,key 为缩略图名称,value 为 OSS 处理参数 |
21
62
 
22
- | 选项 | 类型 | 必须 | 说明 |
23
- | --------------- | ---------------- | ---- | ---------------------------- |
24
- | root | string | | 以OSS子目录作为存储根路径,例如 '/uploads' |
25
- | urlPrefix | string | | URL前缀,用于生成下载链接 |
26
- | publicRead | boolean | | bucket是否允许公共读 |
27
- | accessKeyId | string | Yes | OSS访问KEY |
28
- | accessKeySecret | string | Yes | OSS访问秘钥 |
29
- | bucket | string | | |
30
- | region | string | | |
31
- | internal | boolean | | |
32
- | secure | boolean | | |
33
- | timeout | string \| number | | |
34
- | accountId | string | | 阿里云账号,用于边缘上传生成STS角色扮演令牌 |
35
- | roleName | string | | 阿里云角色名,用于边缘上传生成STS角色扮演令牌 |
36
- | callbackUrl | string | | 边缘上传后的回调地址 |
37
63
 
38
- OSS 相关参数设置参考 https://github.com/ali-sdk/ali-oss#ossoptions
64
+ ## License
39
65
 
66
+ MIT
package/index.d.ts CHANGED
@@ -1,78 +1,525 @@
1
1
  import { Adapter } from 'fsd';
2
2
 
3
+ /**
4
+ * OSSAdapter 配置选项
5
+ *
6
+ * 阿里云 OSS 对象存储适配器的初始化配置。
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const adapter = new OSSAdapter({
11
+ * accessKeyId: 'your-access-key-id',
12
+ * accessKeySecret: 'your-access-key-secret',
13
+ * region: 'oss-cn-hangzhou',
14
+ * bucket: 'my-bucket'
15
+ * });
16
+ * ```
17
+ */
3
18
  export interface OSSAdapterOptions {
19
+ /**
20
+ * OSS 存储根路径(可选)
21
+ *
22
+ * 以 OSS 子目录作为存储根路径。
23
+ * 所有文件操作都会在此路径下进行。
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * root: '/uploads'
28
+ * // 文件路径: '/uploads/file.txt'
29
+ * // OSS 实际路径: 'uploads/file.txt'
30
+ * ```
31
+ */
4
32
  root?: string;
33
+
34
+ /**
35
+ * URL 前缀(可选)
36
+ *
37
+ * 用于生成访问链接时添加前缀。
38
+ * 通常配合 CDN 或反向代理使用。
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * urlPrefix: 'https://cdn.example.com',
43
+ * // file.createUrl() 返回: https://cdn.example.com/uploads/file.txt
44
+ * ```
45
+ */
5
46
  urlPrefix?: string;
47
+
6
48
  /**
7
- * 是否公开读,默认为false
49
+ * 是否公共读(可选)
50
+ *
51
+ * 控制生成的 URL 是否需要签名访问。
52
+ *
53
+ * - `true`: 公共 Bucket,生成直接访问 URL(无需签名)
54
+ * - `false`: 私有 Bucket,生成带签名的临时 URL(默认值)
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * // 公共访问
59
+ * publicRead: true,
60
+ * // URL: https://bucket.oss-cn-hangzhou.aliyuncs.com/file.jpg
61
+ *
62
+ * // 私有访问
63
+ * publicRead: false,
64
+ * // URL: https://bucket.oss-cn-hangzhou.aliyuncs.com/file.jpg?OSSAccessKeyId=...&Expires=...
65
+ * ```
8
66
  */
9
67
  publicRead?: boolean;
10
- // 以下为OSS驱动配置
68
+
69
+ /**
70
+ * OSS 访问 Key ID(必需)
71
+ *
72
+ * 阿里云 OSS 的 Access Key ID。
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * accessKeyId: 'LTAI5txxxxxxxxxxxxx'
77
+ * ```
78
+ */
11
79
  accessKeyId: string;
80
+
81
+ /**
82
+ * OSS 访问 Key Secret(必需)
83
+ *
84
+ * 阿里云 OSS 的 Access Key Secret。
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * accessKeySecret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
89
+ * ```
90
+ *
91
+ * @remarks
92
+ * 请妥善保管此密钥,不要提交到代码仓库。
93
+ * 建议使用环境变量存储。
94
+ */
12
95
  accessKeySecret: string;
13
- bucket: string;
96
+
97
+ /**
98
+ * OSS Bucket 名称(可选)
99
+ *
100
+ * 如果不指定,需要在上传时在 path 中包含 bucket 名称。
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * bucket: 'my-bucket',
105
+ * // 文件路径: '/uploads/file.txt'
106
+ * // OSS 实际路径: 'my-bucket/uploads/file.txt'
107
+ * ```
108
+ */
109
+ bucket?: string;
110
+
111
+ /**
112
+ * OSS 区域代码(必需)
113
+ *
114
+ * 阿里云 OSS 的区域代码,如 'oss-cn-hangzhou'。
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * region: 'oss-cn-hangzhou', // 华东1(杭州)
119
+ * region: 'oss-cn-shanghai', // 华东2(上海)
120
+ * region: 'oss-us-west-1' // 美西1(硅谷)
121
+ * ```
122
+ *
123
+ * @remarks
124
+ * 常见区域代码:
125
+ * - oss-cn-hangzhou: 华东1(杭州)
126
+ * - oss-cn-shanghai: 华东2(上海)
127
+ * - oss-cn-beijing: 华北2(北京)
128
+ * - oss-cn-shenzhen: 华南1(深圳)
129
+ * - oss-cn-hongkong: 香港
130
+ * - oss-us-west-1: 美西1(硅谷)
131
+ */
14
132
  region: string;
133
+
134
+ /**
135
+ * 是否内网访问(可选)
136
+ *
137
+ * - `true`: 使用内网地址(更快且免费)
138
+ * - `false`: 使用公网地址(默认值)
139
+ *
140
+ * @example
141
+ * ```typescript
142
+ * // 如果应用和 OSS 在同一区域,使用内网
143
+ * internal: true,
144
+ * // 实际 endpoint: bucket.oss-cn-hangzhou-internal.aliyuncs.com
145
+ * ```
146
+ */
15
147
  internal?: boolean;
148
+
149
+ /**
150
+ * 是否使用 HTTPS(可选)
151
+ *
152
+ * - `true`: 使用 HTTPS(默认值)
153
+ * - `false`: 使用 HTTP
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * secure: true, // https://bucket.oss-cn-hangzhou.aliyuncs.com
158
+ * secure: false, // http://bucket.oss-cn-hangzhou.aliyuncs.com
159
+ * ```
160
+ */
16
161
  secure?: boolean;
162
+
163
+ /**
164
+ * 请求超时时间(可选)
165
+ *
166
+ * 单位:毫秒(数字)或秒(字符串)
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * timeout: 60000, // 60 秒(毫秒)
171
+ * timeout: '60s' // 60 秒(字符串)
172
+ * timeout: 120000 // 120 秒
173
+ * ```
174
+ */
17
175
  timeout?: string | number;
18
- // 角色扮演
176
+
177
+ /**
178
+ * 阿里云账号 ID(可选)
179
+ *
180
+ * 用于 STS 角色扮演,生成边缘上传的临时凭证。
181
+ *
182
+ * @example
183
+ * ```typescript
184
+ * accountId: '1234567890123456',
185
+ * roleName: 'AliyunOSSRole'
186
+ * ```
187
+ *
188
+ * @remarks
189
+ * 当同时提供 `accountId` 和 `roleName` 时,适配器会生成 STS 临时凭证。
190
+ * 临时凭证用于客户端直接上传到 OSS,无需通过服务器中转。
191
+ */
19
192
  accountId?: string;
193
+
194
+ /**
195
+ * 阿里云角色名称(可选)
196
+ *
197
+ * 用于 STS 角色扮演,配合 `accountId` 使用。
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * accountId: '1234567890123456',
202
+ * roleName: 'AliyunOSSRole'
203
+ * ```
204
+ */
20
205
  roleName?: string;
21
- callbackUrl?: string; // 边缘上传回调地址
206
+
22
207
  /**
23
- * 缩略图配置
208
+ * 上传回调 URL(可选)
209
+ *
210
+ * 文件上传完成后,OSS 会调用此 URL 通知应用。
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * callbackUrl: 'https://api.example.com/oss/callback'
215
+ * ```
216
+ *
217
+ * @remarks
218
+ * 回调 body 包含:
219
+ * - bucket: Bucket 名称
220
+ * - path: 文件路径
221
+ * - etag: 文件 ETag
222
+ * - size: 文件大小
223
+ * - mimeType: MIME 类型
224
+ * - imageInfo: 图片信息(如果是图片)
225
+ */
226
+ callbackUrl?: string;
227
+
228
+ /**
229
+ * 缩略图配置(可选)
230
+ *
231
+ * 配置缩略图规格和样式。
232
+ *
233
+ * @example
234
+ * ```typescript
235
+ * thumbs: {
236
+ * 'small': '?x-oss-process=style/small',
237
+ * 'medium': '?x-oss-process=style/medium',
238
+ * 'large': '?x-oss-process=style/large'
239
+ * }
240
+ * ```
241
+ *
242
+ * @remarks
243
+ * 缩略图通过 OSS 图片处理服务生成。
244
+ * 使用时在 `createUrl()` 中指定 thumb 选项。
24
245
  */
25
246
  thumbs?: {
26
- // name -> ?x-oss-process=style/stylename
247
+ /**
248
+ * 缩略图规格名
249
+ *
250
+ * 值为 OSS 图片处理参数。
251
+ * 常用:
252
+ * - `?x-oss-process=image/resize,w_100`: 调整宽度到 100px
253
+ * - `?x-oss-process=image/crop,w_100,h_100`: 裁剪 100x100
254
+ * - `?x-oss-process=style/name`: 使用命名样式
255
+ */
27
256
  [name: string]: string;
28
257
  };
29
258
  }
30
259
 
260
+ /**
261
+ * 上传凭证
262
+ *
263
+ * 包含临时访问凭证的返回值。
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * {
268
+ * auth: {
269
+ * accessKeyId: 'STS.xxxxxxxxxxxxxx',
270
+ * accessKeySecret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
271
+ * stsToken: 'CAESxxxx...',
272
+ * bucket: 'my-bucket',
273
+ * endpoint: 'oss-cn-hangzhou.aliyuncs.com'
274
+ * },
275
+ * path: '/uploads/file.jpg',
276
+ * expiration: 17001234567890,
277
+ * callback: undefined
278
+ * }
279
+ * ```
280
+ */
31
281
  export interface UploadToken {
282
+ /**
283
+ * 认证信息
284
+ *
285
+ * 包含临时的访问密钥和 STS Token。
286
+ */
32
287
  auth: {
288
+ /**
289
+ * 临时 Access Key ID
290
+ */
33
291
  accessKeyId: string;
292
+
293
+ /**
294
+ * 临时 Access Key Secret
295
+ */
34
296
  accessKeySecret: string;
297
+
298
+ /**
299
+ * STS 临时 Token
300
+ */
35
301
  stsToken: string;
302
+
303
+ /**
304
+ * Bucket 名称
305
+ */
36
306
  bucket: string;
307
+
308
+ /**
309
+ * OSS 访问地址
310
+ */
37
311
  endpoint: string;
38
312
  };
313
+
314
+ /**
315
+ * 上传目标路径
316
+ *
317
+ * 文件在 OSS 中的完整路径。
318
+ */
39
319
  path: string;
320
+
321
+ /**
322
+ * 凭证过期时间(Unix 时间戳,秒)
323
+ *
324
+ * 过期后凭证将失效。
325
+ */
40
326
  expiration: number;
327
+
328
+ /**
329
+ * 回调数据(可选)
330
+ *
331
+ * 如果配置了 `callbackUrl`,此项存在。
332
+ */
41
333
  callback?: any;
42
334
  }
43
335
 
336
+ /**
337
+ * 带自动刷新的上传凭证
338
+ *
339
+ * 包含自动刷新 STS Token 的上传凭证。
340
+ *
341
+ * @example
342
+ * ```typescript
343
+ * {
344
+ * auth: {
345
+ * accessKeyId: 'STS.xxxxxxxxxxxxxx',
346
+ * accessKeySecret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
347
+ * stsToken: 'CAESxxxx...',
348
+ * bucket: 'my-bucket',
349
+ * endpoint: 'oss-cn-hangzhou.aliyuncs.com',
350
+ * refreshSTSToken: [Function: refresh]
351
+ * },
352
+ * path: '/uploads/file.jpg',
353
+ * expiration: 17001234567890
354
+ * }
355
+ * ```
356
+ *
357
+ * @remarks
358
+ * 当凭证即将过期时,调用 `refreshSTSToken()` 获取新的临时凭证。
359
+ */
44
360
  export interface UploadTokenWithAutoRefresh {
361
+ /**
362
+ * 认证信息
363
+ */
45
364
  auth: {
365
+ /**
366
+ * 临时 Access Key ID
367
+ */
46
368
  accessKeyId: string;
369
+
370
+ /**
371
+ * 临时 Access Key Secret
372
+ */
47
373
  accessKeySecret: string;
374
+
375
+ /**
376
+ * STS 临时 Token
377
+ */
48
378
  stsToken: string;
379
+
380
+ /**
381
+ * Bucket 名称
382
+ */
49
383
  bucket: string;
384
+
385
+ /**
386
+ * OSS 访问地址
387
+ */
50
388
  endpoint: string;
389
+
390
+ /**
391
+ * 自动刷新 STS Token 的函数
392
+ *
393
+ * 调用此函数获取新的临时凭证。
394
+ *
395
+ * @example
396
+ * ```typescript
397
+ * const newToken = await auth.refreshSTSToken();
398
+ * console.log(newToken.stsToken);
399
+ * ```
400
+ */
51
401
  refreshSTSToken: () => Promise<{
52
402
  accessKeyId: string;
53
403
  accessKeySecret: string;
54
404
  stsToken: string;
55
405
  }>;
56
406
  };
407
+
408
+ /**
409
+ * 上传目标路径
410
+ */
57
411
  path: string;
412
+
413
+ /**
414
+ * 凭证过期时间
415
+ */
58
416
  expiration: number;
417
+
418
+ /**
419
+ * 回调数据(可选)
420
+ */
59
421
  callback?: any;
60
422
  }
61
423
 
62
- export default class OSSAdpter extends Adapter<OSSAdapterOptions> {
424
+ /**
425
+ * 阿里云 OSS 适配器
426
+ *
427
+ * 提供对阿里云 OSS 对象存储的访问能力。
428
+ *
429
+ * @remarks
430
+ * ### 核心特性
431
+ * - 完整的文件 CRUD 操作(create, read, update, delete)
432
+ * - 分段上传(大文件优化)
433
+ * - STS 角色扮演(边缘上传)
434
+ * - 上传回调通知
435
+ * - 缩略图支持
436
+ *
437
+ * ### 不支持的操作
438
+ * - `mkdir()` - OSS 无目录概念
439
+ * - `createReadStream({ start })` - 不支持 start 选项
440
+ *
441
+ * ### 已废弃的选项
442
+ * - `endpoint` - 已废弃,使用 `region/[internal]/[secure]` 代替
443
+ *
444
+ * ### 自定义 OSS 客户端
445
+ * - 内部使用 `SimpleOSSClient` 封装阿里云 OSS API
446
+ *
447
+ * ### STS 角色扮演
448
+ * - 配置 `accountId` + `roleName` 时自动启用
449
+ * - 用于客户端直接上传到 OSS(边缘上传)
450
+ *
451
+ * @example
452
+ * ```typescript
453
+ * import OSSAdapter from 'fsd-oss';
454
+ * import FSD from 'fsd';
455
+ *
456
+ * // 基础配置
457
+ * const adapter = new OSSAdapter({
458
+ * accessKeyId: process.env.OSS_ACCESS_KEY_ID,
459
+ * accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
460
+ * region: process.env.OSS_REGION,
461
+ * bucket: process.env.OSS_BUCKET
462
+ * });
463
+ *
464
+ * const fsd = FSD({ adapter });
465
+ *
466
+ * // 上传文件
467
+ * await fsd('/uploads/file.jpg').write(buffer);
468
+ *
469
+ * // 生成访问 URL
470
+ * const url = await fsd('/uploads/file.jpg').createUrl({ expires: 3600 });
471
+ * ```
472
+ */
473
+ export default class OSSAdapter extends Adapter<OSSAdapterOptions> {
63
474
  /**
64
475
  * 创建上传凭证
65
- * @param {string} path 文件路径
66
- * @param {any} [meta] 文件元信息
67
- * @param {number} [durationSeconds] 上传凭证有效期,单位秒, 默认 3600
476
+ *
477
+ * 生成用于客户端直接上传到 OSS 的临时凭证。
478
+ *
479
+ * @param path - 文件路径
480
+ * @param meta - 文件元数据(可选)
481
+ * @param durationSeconds - 凭证有效期(秒),默认 3600(1 小时)
482
+ * @returns 上传凭证对象
483
+ *
484
+ * @example
485
+ * ```typescript
486
+ * // 生成 1 小时有效的上传凭证
487
+ * const token = await adapter.createUploadToken(
488
+ * '/uploads/photo.jpg',
489
+ * { contentType: 'image/jpeg' },
490
+ * 3600
491
+ * );
492
+ *
493
+ * // 将 token 发送给前端
494
+ * res.json({ uploadToken: token });
495
+ * ```
496
+ *
497
+ * @remarks
498
+ * 如果配置了 `accountId` 和 `roleName`,会生成 STS 临时凭证。
68
499
  */
69
500
  createUploadToken: (path: string, meta?: any, durationSeconds?: number) => Promise<UploadToken>;
70
501
 
71
502
  /**
72
503
  * 创建带自动刷新的上传凭证
73
- * @param {string} path 文件路径
74
- * @param {any} [meta] 文件元信息
75
- * @param {number} [durationSeconds] 上传凭证有效期,单位秒, 默认 3600
504
+ *
505
+ * 生成支持自动刷新 STS Token 的上传凭证。
506
+ *
507
+ * @param path - 文件路径
508
+ * @param meta - 文件元数据(可选)
509
+ * @param durationSeconds - 凭证有效期(秒),默认 3600(1 小时)
510
+ * @returns 带刷新功能的上传凭证对象
511
+ *
512
+ * @example
513
+ * ```typescript
514
+ * const token = await adapter.createUploadTokenWithAutoRefresh(
515
+ * '/uploads/large-video.mp4',
516
+ * { contentType: 'video/mp4' },
517
+ * 3600
518
+ * );
519
+ *
520
+ * // 当凭证快过期时,自动刷新
521
+ * const newAuth = await token.auth.refreshSTSToken();
522
+ * ```
76
523
  */
77
524
  createUploadTokenWithAutoRefresh: (
78
525
  path: string,
package/lib/index.js CHANGED
@@ -1,15 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const Path = require("path");
4
- const qs = require("qs");
5
- const slash = require("slash");
6
- const minimatch = require("minimatch");
7
- const Debugger = require("debug");
8
- const eachLimit = require("async/eachLimit");
9
- const RPC = require("@alicloud/pop-core");
10
- const simple_oss_client_1 = require("./simple-oss-client");
3
+ const tslib_1 = require("tslib");
4
+ const path_1 = tslib_1.__importDefault(require("path"));
5
+ const qs_1 = tslib_1.__importDefault(require("qs"));
6
+ const slash_1 = tslib_1.__importDefault(require("slash"));
7
+ const minimatch_1 = require("minimatch");
8
+ const debug_1 = tslib_1.__importDefault(require("debug"));
9
+ const eachLimit_1 = tslib_1.__importDefault(require("async/eachLimit"));
10
+ const pop_core_1 = tslib_1.__importDefault(require("@alicloud/pop-core"));
11
+ const simple_oss_client_1 = tslib_1.__importDefault(require("./simple-oss-client"));
11
12
  const stream_1 = require("stream");
12
- const debug = Debugger('fsd-oss');
13
+ const debug = (0, debug_1.default)('fsd-oss');
13
14
  const CALLBACK_BODY = 'bucket=${bucket}&path=${object}&etag=${etag}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}&format=${imageInfo.format}';
14
15
  class OSSAdapter {
15
16
  constructor(options) {
@@ -41,7 +42,7 @@ class OSSAdapter {
41
42
  if (options.region) {
42
43
  stsEndpoint = `https://sts.${options.region.replace('oss-', '')}.aliyuncs.com`;
43
44
  }
44
- this._rpc = new RPC({
45
+ this._rpc = new pop_core_1.default({
45
46
  accessKeyId: options.accessKeyId,
46
47
  accessKeySecret: options.accessKeySecret,
47
48
  endpoint: stsEndpoint,
@@ -51,7 +52,7 @@ class OSSAdapter {
51
52
  this.createUploadToken = async (path, meta, durationSeconds) => {
52
53
  if (!options.accountId || !options.roleName)
53
54
  throw new Error('Can not create sts token, missing options: accountId and roleName!');
54
- path = slash(Path.join(options.root, path)).substring(1);
55
+ path = (0, slash_1.default)(path_1.default.join(options.root, path)).substring(1);
55
56
  let params = {
56
57
  RoleArn: `acs:ram::${options.accountId}:role/${options.roleName}`,
57
58
  RoleSessionName: 'fsd',
@@ -109,7 +110,7 @@ class OSSAdapter {
109
110
  async append(path, data) {
110
111
  debug('append %s', path);
111
112
  const { root } = this._options;
112
- let p = slash(Path.join(root, path)).substring(1);
113
+ let p = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
113
114
  if (typeof data === 'string') {
114
115
  data = Buffer.from(data);
115
116
  }
@@ -123,7 +124,7 @@ class OSSAdapter {
123
124
  async createReadStream(path, options) {
124
125
  debug('createReadStream %s options: %o', path, options);
125
126
  const { root } = this._options;
126
- let p = slash(Path.join(root, path)).substring(1);
127
+ let p = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
127
128
  let opts = {};
128
129
  if (options) {
129
130
  let start = options.start || 0;
@@ -146,7 +147,7 @@ class OSSAdapter {
146
147
  if (options?.start)
147
148
  throw new Error('fsd-oss read stream does not support start options');
148
149
  const { root } = this._options;
149
- let p = slash(Path.join(root, path)).substring(1);
150
+ let p = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
150
151
  let stream = new stream_1.PassThrough();
151
152
  stream.promise = this._oss.put(p, stream);
152
153
  return stream;
@@ -154,7 +155,7 @@ class OSSAdapter {
154
155
  async unlink(path) {
155
156
  debug('unlink %s', path);
156
157
  const { root } = this._options;
157
- let p = slash(Path.join(root, path)).substring(1);
158
+ let p = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
158
159
  if (path.endsWith('/')) {
159
160
  let continuationToken = '';
160
161
  do {
@@ -178,7 +179,7 @@ class OSSAdapter {
178
179
  }
179
180
  async mkdir(path, recursive) {
180
181
  debug('mkdir %s', path);
181
- let parent = Path.dirname(path);
182
+ let parent = path_1.default.dirname(path);
182
183
  if (recursive && parent !== '/') {
183
184
  parent += '/';
184
185
  if (!(await this.exists(parent))) {
@@ -187,7 +188,7 @@ class OSSAdapter {
187
188
  }
188
189
  }
189
190
  const { root } = this._options;
190
- let p = slash(Path.join(root, path)).substring(1);
191
+ let p = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
191
192
  let res = await this._oss.put(p, Buffer.from(''));
192
193
  debug('mkdir result: %O', res);
193
194
  }
@@ -202,7 +203,7 @@ class OSSAdapter {
202
203
  pattern = recursion;
203
204
  }
204
205
  const { root } = this._options;
205
- let p = slash(Path.join(root, path)).substring(1);
206
+ let p = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
206
207
  let results = Object.create(null);
207
208
  let continuationToken = '';
208
209
  let hasContents = false;
@@ -219,12 +220,12 @@ class OSSAdapter {
219
220
  if (list.Contents) {
220
221
  hasContents = true;
221
222
  list.Contents.forEach((object) => {
222
- let relative = slash(Path.relative(p, object.Key));
223
+ let relative = (0, slash_1.default)(path_1.default.relative(p, object.Key));
223
224
  if (!relative)
224
225
  return;
225
226
  if (object.Key.endsWith('/'))
226
227
  relative += '/';
227
- if (pattern && pattern !== '**/*' && !minimatch(relative, pattern))
228
+ if (pattern && pattern !== '**/*' && !(0, minimatch_1.minimatch)(relative, pattern))
228
229
  return;
229
230
  results[relative] = {
230
231
  name: relative,
@@ -238,7 +239,7 @@ class OSSAdapter {
238
239
  if (list.CommonPrefixes) {
239
240
  hasCommonPrefixes = true;
240
241
  list.CommonPrefixes.forEach((prefix) => {
241
- let relative = slash(Path.relative(p, prefix.Prefix));
242
+ let relative = (0, slash_1.default)(path_1.default.relative(p, prefix.Prefix));
242
243
  if (!relative)
243
244
  return;
244
245
  relative += '/';
@@ -259,7 +260,7 @@ class OSSAdapter {
259
260
  debug('createUrl %s', path);
260
261
  options = Object.assign({}, options);
261
262
  const { root, urlPrefix, publicRead } = this._options;
262
- let p = slash(Path.join(root, path));
263
+ let p = (0, slash_1.default)(path_1.default.join(root, path));
263
264
  let suffix = '';
264
265
  if (options.thumb) {
265
266
  if (options.thumb in this._options.thumbs) {
@@ -275,7 +276,7 @@ class OSSAdapter {
275
276
  if (suffix) {
276
277
  if (suffix[0] === '?')
277
278
  suffix = suffix.substring(1);
278
- options.query = qs.parse(suffix);
279
+ options.query = qs_1.default.parse(suffix);
279
280
  }
280
281
  let url = this._oss.signatureUrl(p.substring(1), options);
281
282
  if (urlPrefix) {
@@ -288,8 +289,8 @@ class OSSAdapter {
288
289
  if (!(await this.exists(path)))
289
290
  throw new Error('The source path is not exists!');
290
291
  const { root } = this._options;
291
- let from = slash(Path.join(root, path)).substring(1);
292
- let to = slash(Path.join(root, dest)).substring(1);
292
+ let from = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
293
+ let to = (0, slash_1.default)(path_1.default.join(root, dest)).substring(1);
293
294
  if (path.endsWith('/')) {
294
295
  debug('copy directory %s -> %s', from, to);
295
296
  let continuationToken = '';
@@ -302,10 +303,10 @@ class OSSAdapter {
302
303
  debug('list result: %O', list);
303
304
  continuationToken = list.NextContinuationToken;
304
305
  if (list.Contents?.length) {
305
- await eachLimit(list.Contents, 10, async (object) => {
306
+ await (0, eachLimit_1.default)(list.Contents, 10, async (object) => {
306
307
  debug(' -> copy %s', object.Key);
307
- let relative = slash(Path.relative(from, object.Key));
308
- let target = slash(Path.join(to, relative));
308
+ let relative = (0, slash_1.default)(path_1.default.relative(from, object.Key));
309
+ let target = (0, slash_1.default)(path_1.default.join(to, relative));
309
310
  await this._oss.copy(target, object.Key);
310
311
  });
311
312
  }
@@ -328,7 +329,7 @@ class OSSAdapter {
328
329
  async exists(path) {
329
330
  debug('check exists %s', path);
330
331
  const { root } = this._options;
331
- let p = slash(Path.join(root, path)).substring(1);
332
+ let p = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
332
333
  if (path.endsWith('/')) {
333
334
  let list = await this._oss.list({
334
335
  prefix: p,
@@ -347,7 +348,7 @@ class OSSAdapter {
347
348
  async isFile(path) {
348
349
  debug('check is file %s', path);
349
350
  const { root } = this._options;
350
- let p = slash(Path.join(root, path)).substring(1);
351
+ let p = (0, slash_1.default)(path_1.default.join(root, path)).substring(1);
351
352
  try {
352
353
  await this._oss.head(p);
353
354
  return true;
@@ -358,7 +359,7 @@ class OSSAdapter {
358
359
  }
359
360
  async isDirectory(path) {
360
361
  debug('check is directory %s', path);
361
- let p = slash(Path.join(this._options.root, path)).substring(1);
362
+ let p = (0, slash_1.default)(path_1.default.join(this._options.root, path)).substring(1);
362
363
  try {
363
364
  await this._oss.head(p);
364
365
  return true;
@@ -369,20 +370,20 @@ class OSSAdapter {
369
370
  }
370
371
  async size(path) {
371
372
  debug('get file size %s', path);
372
- let p = slash(Path.join(this._options.root, path)).substring(1);
373
+ let p = (0, slash_1.default)(path_1.default.join(this._options.root, path)).substring(1);
373
374
  let res = await this._oss.head(p);
374
375
  return parseInt(res.headers.get('Content-Length')) || 0;
375
376
  }
376
377
  async lastModified(path) {
377
378
  debug('get file lastModified %s', path);
378
- let p = slash(Path.join(this._options.root, path)).substring(1);
379
+ let p = (0, slash_1.default)(path_1.default.join(this._options.root, path)).substring(1);
379
380
  let res = await this._oss.head(p);
380
381
  let headers = res.headers;
381
382
  return new Date(headers.get('Last-Modified'));
382
383
  }
383
384
  async initMultipartUpload(path, partCount) {
384
385
  debug('initMultipartUpload %s, partCount: %d', path, partCount);
385
- let p = slash(Path.join(this._options.root, path)).substring(1);
386
+ let p = (0, slash_1.default)(path_1.default.join(this._options.root, path)).substring(1);
386
387
  let { UploadId } = await this._oss.initMultipartUpload(p);
387
388
  let files = [];
388
389
  for (let i = 1; i <= partCount; i += 1) {
@@ -392,7 +393,7 @@ class OSSAdapter {
392
393
  }
393
394
  async writePart(path, partTask, data, size) {
394
395
  debug('writePart %s, task: %s', path, partTask);
395
- let p = slash(Path.join(this._options.root, path)).substring(1);
396
+ let p = (0, slash_1.default)(path_1.default.join(this._options.root, path)).substring(1);
396
397
  if (!partTask.startsWith('task://'))
397
398
  throw new Error('Invalid part task id');
398
399
  let [uploadId, no] = partTask.replace('task://', '').split('?');
@@ -407,7 +408,7 @@ class OSSAdapter {
407
408
  async completeMultipartUpload(path, parts) {
408
409
  debug('completeMultipartUpload %s', path);
409
410
  let uploadId = parts[0].replace('part://', '').split('?')[0];
410
- let p = slash(Path.join(this._options.root, path)).substring(1);
411
+ let p = (0, slash_1.default)(path_1.default.join(this._options.root, path)).substring(1);
411
412
  debug('update id: %s, target: %s', uploadId, p);
412
413
  let datas = parts.map((item, key) => ({
413
414
  etag: item.split('#')[1],
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const qs = require("qs");
4
- const xml2js = require("xml2js");
5
- const sha1 = require("crypto-js/hmac-sha1");
6
- const md5 = require("crypto-js/md5");
7
- const Base64Encoder = require("crypto-js/enc-base64");
8
- const mime = require("mime-types");
9
- const akita_1 = require("akita");
3
+ const tslib_1 = require("tslib");
4
+ const qs_1 = tslib_1.__importDefault(require("qs"));
5
+ const xml2js_1 = tslib_1.__importDefault(require("xml2js"));
6
+ const hmac_sha1_1 = tslib_1.__importDefault(require("crypto-js/hmac-sha1"));
7
+ const md5_1 = tslib_1.__importDefault(require("crypto-js/md5"));
8
+ const enc_base64_1 = tslib_1.__importDefault(require("crypto-js/enc-base64"));
9
+ const mime_types_1 = tslib_1.__importDefault(require("mime-types"));
10
+ const akita_1 = tslib_1.__importDefault(require("akita"));
10
11
  const client = akita_1.default.create({});
11
12
  class SimpleOSSClient {
12
13
  constructor(config) {
@@ -23,7 +24,7 @@ class SimpleOSSClient {
23
24
  options = options || {};
24
25
  let position = options.position || 0;
25
26
  if (!options.mime)
26
- options.mime = mime.lookup(name) || 'application/octet-stream';
27
+ options.mime = mime_types_1.default.lookup(name) || 'application/octet-stream';
27
28
  return this.requestData('POST', `${name}?append&position=${position}`, body, options);
28
29
  }
29
30
  get(name, options) {
@@ -32,7 +33,7 @@ class SimpleOSSClient {
32
33
  put(name, body, options) {
33
34
  options = options || {};
34
35
  if (!options.mime)
35
- options.mime = mime.lookup(name) || 'application/octet-stream';
36
+ options.mime = mime_types_1.default.lookup(name) || 'application/octet-stream';
36
37
  return this.requestData('PUT', name, body, options);
37
38
  }
38
39
  async copy(to, from, options) {
@@ -95,7 +96,7 @@ class SimpleOSSClient {
95
96
  del(name, options) {
96
97
  options = options || {};
97
98
  if (!options.mime)
98
- options.mime = mime.lookup(name) || 'application/octet-stream';
99
+ options.mime = mime_types_1.default.lookup(name) || 'application/octet-stream';
99
100
  return this.requestData('DELETE', name, null, options);
100
101
  }
101
102
  async deleteMulti(names, options) {
@@ -105,7 +106,7 @@ class SimpleOSSClient {
105
106
  options.mime = 'application/xml';
106
107
  if (!options.headers)
107
108
  options.headers = {};
108
- options.headers['Content-MD5'] = md5(body).toString(Base64Encoder);
109
+ options.headers['Content-MD5'] = (0, md5_1.default)(body).toString(enc_base64_1.default);
109
110
  let res = await this.requestData('POST', '?delete', body, options);
110
111
  if (res.Deleted) {
111
112
  if (!Array.isArray(res.Deleted))
@@ -120,13 +121,13 @@ class SimpleOSSClient {
120
121
  async initMultipartUpload(name, options) {
121
122
  options = options || {};
122
123
  if (!options.mime)
123
- options.mime = mime.lookup(name) || 'application/octet-stream';
124
+ options.mime = mime_types_1.default.lookup(name) || 'application/octet-stream';
124
125
  return await this.requestData('POST', `${name}?uploads`, '', options);
125
126
  }
126
127
  uploadPart(name, uploadId, partNumber, body, options) {
127
128
  options = options || {};
128
129
  if (!options.mime)
129
- options.mime = mime.lookup(name) || 'application/octet-stream';
130
+ options.mime = mime_types_1.default.lookup(name) || 'application/octet-stream';
130
131
  return this.requestData('PUT', `${name}?partNumber=${partNumber}&uploadId=${uploadId}`, body, options);
131
132
  }
132
133
  async completeMultipartUpload(name, uploadId, parts, options) {
@@ -173,7 +174,7 @@ class SimpleOSSClient {
173
174
  headers: response.headers
174
175
  };
175
176
  }
176
- let data = await xml2js.parseStringPromise(xml, {
177
+ let data = await xml2js_1.default.parseStringPromise(xml, {
177
178
  trim: true,
178
179
  explicitArray: false,
179
180
  explicitRoot: false
@@ -202,12 +203,12 @@ class SimpleOSSClient {
202
203
  query['security-token'] = this.config.stsToken;
203
204
  }
204
205
  let parts = [options.method || 'GET', '', '', String(expires)];
205
- let string = qs.stringify(query);
206
+ let string = qs_1.default.stringify(query);
206
207
  parts.push(`/${this.config.bucket}/${name}${string ? `?${string}` : ''}`);
207
- query.Signature = sha1(parts.join('\n'), this.config.accessKeySecret).toString(Base64Encoder);
208
+ query.Signature = (0, hmac_sha1_1.default)(parts.join('\n'), this.config.accessKeySecret).toString(enc_base64_1.default);
208
209
  query.OSSAccessKeyId = this.config.accessKeyId;
209
210
  query.Expires = expires;
210
- return `${url}?${qs.stringify(query)}`;
211
+ return `${url}?${qs_1.default.stringify(query)}`;
211
212
  }
212
213
  createCanonicalizedResource(resource, subres) {
213
214
  if (!subres)
@@ -235,7 +236,7 @@ class SimpleOSSClient {
235
236
  parts.push(`${key}:${headers[key]}`);
236
237
  });
237
238
  parts.push(canonicalizedResource);
238
- let sign = sha1(parts.join('\n'), this.config.accessKeySecret).toString(Base64Encoder);
239
+ let sign = (0, hmac_sha1_1.default)(parts.join('\n'), this.config.accessKeySecret).toString(enc_base64_1.default);
239
240
  return `OSS ${this.config.accessKeyId}:${sign}`;
240
241
  }
241
242
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fsd-oss",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Aliyun OSS adapter for fsd",
5
5
  "main": "lib/index.js",
6
6
  "types": "index.d.ts",
@@ -9,18 +9,22 @@
9
9
  "prepublish": "npm run build"
10
10
  },
11
11
  "repository": "https://github.com/liangxingchen/fsd/tree/master/packages/fsd-oss",
12
- "author": "Liang <liang@miaomo.cc> (https://github.com/liangxingchen)",
12
+ "author": {
13
+ "name": "Liang",
14
+ "email": "liang@miaomo.cn",
15
+ "url": "https://github.com/liangxingchen"
16
+ },
13
17
  "license": "MIT",
14
18
  "dependencies": {
15
19
  "@alicloud/pop-core": "^1.8.0",
16
- "akita": "^1.1.0",
20
+ "akita": "^1.2.0",
17
21
  "async": "*",
18
22
  "crypto-js": "^4.2.0",
19
- "debug": "^4.4.0",
20
- "mime-types": "^2.1.35",
21
- "minimatch": "^3.1.2",
23
+ "debug": "^4.4.3",
24
+ "mime-types": "^3.0.2",
25
+ "minimatch": "^9.0.5",
22
26
  "slash": "^3.0.0",
23
27
  "xml2js": "^0.6.2"
24
28
  },
25
- "gitHead": "74e32bf47242909f040eb6012dda56e5c5a668a0"
29
+ "gitHead": "ea754919fb95b49deffd529b5c01c66da0dc08f9"
26
30
  }