xin_oss-upload 1.0.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/README.md +299 -0
- package/dist/index.d.mts +188 -0
- package/dist/index.d.ts +188 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# @xin/oss-upload
|
|
2
|
+
|
|
3
|
+
多厂商 OSS 文件上传工具库,支持阿里云 OSS、腾讯云 COS、七牛云,提供统一的 API 接口。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @xin/oss-upload
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 快速开始
|
|
12
|
+
|
|
13
|
+
### 工厂函数(推荐)
|
|
14
|
+
|
|
15
|
+
使用 `createOSSUploader` 根据配置自动创建对应厂商的上传器:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { createOSSUploader } from '@xin/oss-upload'
|
|
19
|
+
|
|
20
|
+
const uploader = createOSSUploader({
|
|
21
|
+
vendor: 'aliyun',
|
|
22
|
+
region: 'oss-cn-hangzhou',
|
|
23
|
+
accessKeyId: 'YOUR_ACCESS_KEY_ID',
|
|
24
|
+
accessKeySecret: 'YOUR_ACCESS_KEY_SECRET',
|
|
25
|
+
bucket: 'your-bucket',
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const result = await uploader.uploadFile('/path/to/local/file.jpg', {
|
|
29
|
+
targetPath: 'images/file.jpg',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
console.log(result.url) // 访问 URL
|
|
33
|
+
console.log(result.size) // 文件大小(字节)
|
|
34
|
+
console.log(result.costTime) // 上传耗时(ms)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 直接实例化
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { AliyunOSSUploader } from '@xin/oss-upload'
|
|
41
|
+
|
|
42
|
+
const uploader = new AliyunOSSUploader({ ... })
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 配置项
|
|
48
|
+
|
|
49
|
+
### 通用配置(所有厂商)
|
|
50
|
+
|
|
51
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
52
|
+
| -------------- | --------- | ---- | ------------------------- |
|
|
53
|
+
| `vendor` | `string` | ✅ | 厂商标识,见下方各厂商配置 |
|
|
54
|
+
| `secure` | `boolean` | — | 是否使用 HTTPS,默认 `true` |
|
|
55
|
+
| `customDomain` | `string` | — | 自定义 CDN 域名(不含协议)|
|
|
56
|
+
|
|
57
|
+
### 阿里云 OSS — `vendor: 'aliyun'`
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { createOSSUploader } from '@xin/oss-upload'
|
|
61
|
+
|
|
62
|
+
const uploader = createOSSUploader({
|
|
63
|
+
vendor: 'aliyun',
|
|
64
|
+
region: 'oss-cn-hangzhou', // OSS 地域
|
|
65
|
+
accessKeyId: 'YOUR_KEY_ID',
|
|
66
|
+
accessKeySecret: 'YOUR_KEY_SECRET',
|
|
67
|
+
bucket: 'your-bucket',
|
|
68
|
+
endpoint: 'oss-cn-hangzhou.aliyuncs.com', // 可选,自定义 Endpoint
|
|
69
|
+
customDomain: 'cdn.example.com', // 可选,自定义域名
|
|
70
|
+
})
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
74
|
+
| ----------------- | -------- | ---- | ----------------- |
|
|
75
|
+
| `region` | `string` | ✅ | OSS 地域 |
|
|
76
|
+
| `accessKeyId` | `string` | ✅ | AccessKey ID |
|
|
77
|
+
| `accessKeySecret` | `string` | ✅ | AccessKey Secret |
|
|
78
|
+
| `bucket` | `string` | ✅ | 存储空间名称 |
|
|
79
|
+
| `endpoint` | `string` | — | 自定义 Endpoint |
|
|
80
|
+
|
|
81
|
+
### 腾讯云 COS — `vendor: 'tencent'`
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const uploader = createOSSUploader({
|
|
85
|
+
vendor: 'tencent',
|
|
86
|
+
region: 'ap-guangzhou', // COS 地域
|
|
87
|
+
secretId: 'YOUR_SECRET_ID',
|
|
88
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
89
|
+
bucket: 'your-bucket', // 不含 AppId 的桶名
|
|
90
|
+
appId: '1250000000', // 腾讯云 AppId
|
|
91
|
+
customDomain: 'cdn.example.com', // 可选
|
|
92
|
+
})
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
> 完整存储桶名称由 `bucket-appId` 拼接,SDK 内部自动处理。
|
|
96
|
+
|
|
97
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
98
|
+
| ----------- | -------- | ---- | ----------------- |
|
|
99
|
+
| `region` | `string` | ✅ | COS 地域 |
|
|
100
|
+
| `secretId` | `string` | ✅ | SecretId |
|
|
101
|
+
| `secretKey` | `string` | ✅ | SecretKey |
|
|
102
|
+
| `bucket` | `string` | ✅ | 存储桶名(不含 AppId)|
|
|
103
|
+
| `appId` | `string` | ✅ | 腾讯云 AppId |
|
|
104
|
+
|
|
105
|
+
### 七牛云 — `vendor: 'qiniu'`
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const uploader = createOSSUploader({
|
|
109
|
+
vendor: 'qiniu',
|
|
110
|
+
zone: 'z0', // 存储区域
|
|
111
|
+
accessKey: 'YOUR_ACCESS_KEY',
|
|
112
|
+
secretKey: 'YOUR_SECRET_KEY',
|
|
113
|
+
bucket: 'your-bucket',
|
|
114
|
+
domain: 'cdn.example.com', // 空间绑定的域名(必填)
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
| 字段 | 类型 | 必填 | 说明 |
|
|
119
|
+
| ----------- | -------- | ---- | --------------------- |
|
|
120
|
+
| `zone` | `string` | ✅ | 存储区域,见下方枚举 |
|
|
121
|
+
| `accessKey` | `string` | ✅ | AccessKey |
|
|
122
|
+
| `secretKey` | `string` | ✅ | SecretKey |
|
|
123
|
+
| `bucket` | `string` | ✅ | 存储空间名称 |
|
|
124
|
+
| `domain` | `string` | ✅ | 空间绑定的访问域名 |
|
|
125
|
+
|
|
126
|
+
**zone 枚举值:**
|
|
127
|
+
|
|
128
|
+
| 值 | 区域 |
|
|
129
|
+
| ----- | ------ |
|
|
130
|
+
| `z0` | 华东 |
|
|
131
|
+
| `z1` | 华北 |
|
|
132
|
+
| `z2` | 华南 |
|
|
133
|
+
| `na0` | 北美 |
|
|
134
|
+
| `as0` | 东南亚 |
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 上传选项 `UploadOptions`
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
await uploader.uploadFile('/local/path/file.jpg', {
|
|
142
|
+
targetPath: 'images/2024/file.jpg', // 必填,OSS 上的目标路径
|
|
143
|
+
overwrite: false, // 是否覆盖同名文件,默认 false
|
|
144
|
+
timeout: 60000, // 超时时间(ms),默认 60000
|
|
145
|
+
partSize: 10 * 1024 * 1024, // 分片大小(字节),默认 10MB
|
|
146
|
+
parallel: 5, // 分片并发数,默认 5
|
|
147
|
+
onProgress: (percent) => { // 进度回调,0-100
|
|
148
|
+
console.log(`上传进度: ${percent}%`)
|
|
149
|
+
},
|
|
150
|
+
})
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| 字段 | 类型 | 必填 | 默认值 | 说明 |
|
|
154
|
+
| ------------ | ----------------------------- | ---- | -------- | -------------- |
|
|
155
|
+
| `targetPath` | `string` | ✅ | — | OSS 目标路径 |
|
|
156
|
+
| `overwrite` | `boolean` | — | `false` | 是否覆盖已有文件 |
|
|
157
|
+
| `timeout` | `number` | — | `60000` | 超时(ms) |
|
|
158
|
+
| `partSize` | `number` | — | `10MB` | 分片大小 |
|
|
159
|
+
| `parallel` | `number` | — | `5` | 分片并发数 |
|
|
160
|
+
| `onProgress` | `(percent: number) => void` | — | — | 进度回调(0-100)|
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 上传结果 `UploadResult`
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
interface UploadResult {
|
|
168
|
+
url: string // 文件访问 URL
|
|
169
|
+
path: string // OSS 上的存储路径
|
|
170
|
+
size: number // 文件大小(字节)
|
|
171
|
+
costTime: number // 上传耗时(ms)
|
|
172
|
+
md5?: string // 文件 MD5(部分厂商提供)
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 方法
|
|
179
|
+
|
|
180
|
+
所有上传器均继承自 `BaseOSSUploader`,提供以下方法:
|
|
181
|
+
|
|
182
|
+
### `uploadFile(filePath, options)`
|
|
183
|
+
|
|
184
|
+
上传本地文件到 OSS。
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
const result = await uploader.uploadFile('/local/file.jpg', {
|
|
188
|
+
targetPath: 'uploads/file.jpg',
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `deleteFile(objectName)`
|
|
193
|
+
|
|
194
|
+
删除 OSS 上的文件,文件不存在时也返回 `true`。
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
const success = await uploader.deleteFile('uploads/file.jpg')
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### `getFileUrl(objectName)`
|
|
201
|
+
|
|
202
|
+
获取文件的访问 URL(不包含签名,适用于公开读 Bucket)。
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const url = uploader.getFileUrl('uploads/file.jpg')
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
### `destroy()`
|
|
209
|
+
|
|
210
|
+
释放客户端资源。
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
uploader.destroy()
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 错误处理
|
|
219
|
+
|
|
220
|
+
所有错误均以 `OSSUploadError` 抛出:
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import { OSSUploadError } from '@xin/oss-upload'
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
await uploader.uploadFile('/path/to/file.jpg', {
|
|
227
|
+
targetPath: 'images/file.jpg',
|
|
228
|
+
})
|
|
229
|
+
} catch (err) {
|
|
230
|
+
if (err instanceof OSSUploadError) {
|
|
231
|
+
console.error('错误信息:', err.message)
|
|
232
|
+
console.error('错误码:', err.code)
|
|
233
|
+
console.error('请求 ID:', err.requestId)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**常见错误码:**
|
|
239
|
+
|
|
240
|
+
| `code` | 说明 |
|
|
241
|
+
| ----------------------- | ------------------------ |
|
|
242
|
+
| `INVALID_CONFIG` | 配置项缺失或非法 |
|
|
243
|
+
| `CLIENT_INIT_FAILED` | SDK 客户端初始化失败 |
|
|
244
|
+
| `CLIENT_NOT_INIT` | 客户端未初始化(已销毁) |
|
|
245
|
+
| `NOT_A_FILE` | 指定路径不是文件 |
|
|
246
|
+
| `FILE_EXISTS` | 文件已存在且未开启覆盖 |
|
|
247
|
+
| `CHECK_FILE_FAILED` | 检查文件存在性失败 |
|
|
248
|
+
| `UPLOAD_FAILED` | 上传失败 |
|
|
249
|
+
| `DELETE_FAILED` | 删除失败 |
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 完整示例
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
import { createOSSUploader, OSSUploadError } from '@xin/oss-upload'
|
|
257
|
+
|
|
258
|
+
// 1. 创建上传器
|
|
259
|
+
const uploader = createOSSUploader({
|
|
260
|
+
vendor: 'tencent',
|
|
261
|
+
region: 'ap-guangzhou',
|
|
262
|
+
secretId: process.env.COS_SECRET_ID!,
|
|
263
|
+
secretKey: process.env.COS_SECRET_KEY!,
|
|
264
|
+
bucket: 'my-bucket',
|
|
265
|
+
appId: '1250000000',
|
|
266
|
+
customDomain: 'cdn.example.com',
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
// 2. 上传文件
|
|
270
|
+
try {
|
|
271
|
+
const result = await uploader.uploadFile('./avatar.png', {
|
|
272
|
+
targetPath: `avatars/${Date.now()}.png`,
|
|
273
|
+
overwrite: false,
|
|
274
|
+
onProgress: (p) => console.log(`${p}%`),
|
|
275
|
+
})
|
|
276
|
+
console.log('上传成功:', result.url)
|
|
277
|
+
} catch (err) {
|
|
278
|
+
if (err instanceof OSSUploadError) {
|
|
279
|
+
console.error(`[${err.code}] ${err.message}`)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// 3. 删除文件
|
|
284
|
+
await uploader.deleteFile('avatars/old.png')
|
|
285
|
+
|
|
286
|
+
// 4. 释放资源
|
|
287
|
+
uploader.destroy()
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
## 开发
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
pnpm install # 安装依赖
|
|
296
|
+
pnpm test # 运行测试
|
|
297
|
+
pnpm test:watch # 监听模式
|
|
298
|
+
pnpm build # 构建发布产物
|
|
299
|
+
```
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Stats } from 'fs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description 支持的 OSS 的厂商类型
|
|
5
|
+
*
|
|
6
|
+
* */
|
|
7
|
+
type OSSVendor = 'aliyun' | 'tencent' | 'qiniu';
|
|
8
|
+
/**
|
|
9
|
+
* @description 通用 OSS 配置项 (不同的厂商的配置会在这个配置上扩展)
|
|
10
|
+
* @param vendor: OSSVendor OSS 厂商类型
|
|
11
|
+
* @param secure?: boolean 是否使用 HTTPS
|
|
12
|
+
* @param customDomain?: string 自定义域名
|
|
13
|
+
* */
|
|
14
|
+
interface OSSConfigBase {
|
|
15
|
+
vendor: OSSVendor;
|
|
16
|
+
secure?: boolean;
|
|
17
|
+
customDomain?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @description 阿里云 OSS 配置
|
|
21
|
+
* @param region: string OSS地域
|
|
22
|
+
* @param accessKeyId: string OSS AccessKeyId
|
|
23
|
+
* @param accessKeySecret: string OSS AccessKeySecret
|
|
24
|
+
* @param bucket: string OSS 存储空间名称
|
|
25
|
+
* @param endpoint?: string OSS 域名
|
|
26
|
+
* */
|
|
27
|
+
interface AliyunOSSConfig extends OSSConfigBase {
|
|
28
|
+
vendor: 'aliyun';
|
|
29
|
+
region: string;
|
|
30
|
+
accessKeyId: string;
|
|
31
|
+
accessKeySecret: string;
|
|
32
|
+
bucket: string;
|
|
33
|
+
endpoint?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* @description 腾讯云 COS 配置
|
|
37
|
+
* @param region: string COS 地域
|
|
38
|
+
* @param secretId: string COS AccessKeyId
|
|
39
|
+
* @param secretKey: string COS AccessKeySecret
|
|
40
|
+
* @param bucket: string COS 存储空间名称
|
|
41
|
+
* @param appId: string 腾讯云 AppId
|
|
42
|
+
* */
|
|
43
|
+
interface TencentOSSConfig extends OSSConfigBase {
|
|
44
|
+
vendor: 'tencent';
|
|
45
|
+
region: string;
|
|
46
|
+
secretId: string;
|
|
47
|
+
secretKey: string;
|
|
48
|
+
bucket: string;
|
|
49
|
+
appId: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* @description 七牛云 OSS 配置
|
|
53
|
+
* @param zone: string 七牛云 OSS 地域( z0:华东, z1:华北, z2:华南, na0:北美, as0:东南亚)
|
|
54
|
+
* @param accessKey: string 七牛云 OSS AccessKeyId
|
|
55
|
+
* @param secretKey: string 七牛云 OSS SecretKey
|
|
56
|
+
* @param bucket: string 七牛云 OSS 存储空间名称
|
|
57
|
+
* @param domain: string 七牛云 OSS 域名
|
|
58
|
+
* */
|
|
59
|
+
interface QiniuOSSConfig extends OSSConfigBase {
|
|
60
|
+
vendor: 'qiniu';
|
|
61
|
+
zone: string;
|
|
62
|
+
accessKey: string;
|
|
63
|
+
secretKey: string;
|
|
64
|
+
bucket: string;
|
|
65
|
+
domain: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 联合类型:所有 OSS 配置
|
|
69
|
+
*/
|
|
70
|
+
type OSSConfig = AliyunOSSConfig | TencentOSSConfig | QiniuOSSConfig;
|
|
71
|
+
/**
|
|
72
|
+
* @description 上传的选项
|
|
73
|
+
* @param targetPath: string 上传到 OSS 的目标路径
|
|
74
|
+
* @param overwrite?: boolean 是否覆盖同名文件
|
|
75
|
+
* @param timeout?: number 上传超时时间
|
|
76
|
+
* @param partSize?: number 分片大小
|
|
77
|
+
* @param parallel?: number 分片上传的并发数
|
|
78
|
+
* @param onProgress?: (progress: number) => void 上传进度回调
|
|
79
|
+
**/
|
|
80
|
+
interface UploadOptions {
|
|
81
|
+
targetPath: string;
|
|
82
|
+
overwrite?: boolean;
|
|
83
|
+
timeout?: number;
|
|
84
|
+
partSize?: number;
|
|
85
|
+
parallel?: number;
|
|
86
|
+
onProgress?: (progress: number) => void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* @description 上传结果
|
|
90
|
+
* @param url: string 上传后的 URL
|
|
91
|
+
* @param path: string 上传后的路径
|
|
92
|
+
* @param size: number 上传的文件大小
|
|
93
|
+
* @param costTime: number 上传耗时
|
|
94
|
+
* @param md5?: string 文件的 md5 值
|
|
95
|
+
*/
|
|
96
|
+
interface UploadResult {
|
|
97
|
+
url: string;
|
|
98
|
+
path: string;
|
|
99
|
+
size: number;
|
|
100
|
+
costTime: number;
|
|
101
|
+
md5?: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @description OSS 错误类型
|
|
105
|
+
* */
|
|
106
|
+
declare class OSSUploadError extends Error {
|
|
107
|
+
/** 错误码 */
|
|
108
|
+
code: string;
|
|
109
|
+
/** 请求 ID(部分厂商提供) */
|
|
110
|
+
requestId?: string;
|
|
111
|
+
/** 厂商错误码 */
|
|
112
|
+
vendorCode?: string;
|
|
113
|
+
constructor(message: string, code: string, requestId?: string, vendorCode?: string);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* OSS 上传器抽象基类
|
|
118
|
+
* 定义所有厂商上传器必须实现的核心方法
|
|
119
|
+
*/
|
|
120
|
+
declare abstract class BaseOSSUploader {
|
|
121
|
+
protected config: OSSConfig;
|
|
122
|
+
protected client: any;
|
|
123
|
+
constructor(config: OSSConfig);
|
|
124
|
+
/** 验证配置合法性(子类实现) */
|
|
125
|
+
protected abstract validateConfig(config: OSSConfig): OSSConfig;
|
|
126
|
+
/** 初始化厂商客户端(子类实现) */
|
|
127
|
+
protected abstract initClient(): void;
|
|
128
|
+
/** 上传本地文件 */
|
|
129
|
+
abstract uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;
|
|
130
|
+
/** 删除文件 */
|
|
131
|
+
abstract deleteFile(objectName: string): Promise<boolean>;
|
|
132
|
+
/** 获取文件访问 URL */
|
|
133
|
+
abstract getFileUrl(objectName: string): string;
|
|
134
|
+
/**
|
|
135
|
+
* 检查客户端是否可用,destroy() 后调用会抛出 CLIENT_NOT_INIT
|
|
136
|
+
*/
|
|
137
|
+
protected checkClient(): void;
|
|
138
|
+
/**
|
|
139
|
+
* 校验本地文件路径合法性,返回文件 Stats
|
|
140
|
+
* 路径不存在或不是文件时抛出 NOT_A_FILE
|
|
141
|
+
*/
|
|
142
|
+
protected validateFile(filePath: string): Stats;
|
|
143
|
+
/**
|
|
144
|
+
* 销毁客户端,释放资源
|
|
145
|
+
*/
|
|
146
|
+
destroy(): void;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
declare class AliyunOSSUploader extends BaseOSSUploader {
|
|
150
|
+
protected config: AliyunOSSConfig;
|
|
151
|
+
protected validateConfig(config: AliyunOSSConfig): AliyunOSSConfig;
|
|
152
|
+
protected initClient(): void;
|
|
153
|
+
uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;
|
|
154
|
+
getFileUrl(objectName: string): string;
|
|
155
|
+
deleteFile(objectName: string): Promise<boolean>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
declare class TencentOSSUploader extends BaseOSSUploader {
|
|
159
|
+
protected config: TencentOSSConfig;
|
|
160
|
+
protected validateConfig(config: OSSConfig): TencentOSSConfig;
|
|
161
|
+
protected initClient(): void;
|
|
162
|
+
/** 完整存储桶名称,格式:BucketName-AppId */
|
|
163
|
+
private get fullBucket();
|
|
164
|
+
uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;
|
|
165
|
+
getFileUrl(objectName: string): string;
|
|
166
|
+
deleteFile(objectName: string): Promise<boolean>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
declare class QiniuOSSUploader extends BaseOSSUploader {
|
|
170
|
+
protected config: QiniuOSSConfig;
|
|
171
|
+
protected validateConfig(config: OSSConfig): QiniuOSSConfig;
|
|
172
|
+
protected initClient(): void;
|
|
173
|
+
private getUploadToken;
|
|
174
|
+
uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;
|
|
175
|
+
/**
|
|
176
|
+
* 检查文件是否存在:stat 成功则存在,以 612 拒绝则不存在,其余错误上抛
|
|
177
|
+
*/
|
|
178
|
+
private checkFileExists;
|
|
179
|
+
getFileUrl(objectName: string): string;
|
|
180
|
+
deleteFile(objectName: string): Promise<boolean>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 工厂函数:根据配置中的 vendor 字段自动创建对应厂商的 OSS 上传器
|
|
185
|
+
*/
|
|
186
|
+
declare function createOSSUploader(config: OSSConfig): BaseOSSUploader;
|
|
187
|
+
|
|
188
|
+
export { type AliyunOSSConfig, AliyunOSSUploader, BaseOSSUploader, type OSSConfig, type OSSConfigBase, OSSUploadError, type OSSVendor, type QiniuOSSConfig, QiniuOSSUploader, type TencentOSSConfig, TencentOSSUploader, type UploadOptions, type UploadResult, createOSSUploader };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { Stats } from 'fs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @description 支持的 OSS 的厂商类型
|
|
5
|
+
*
|
|
6
|
+
* */
|
|
7
|
+
type OSSVendor = 'aliyun' | 'tencent' | 'qiniu';
|
|
8
|
+
/**
|
|
9
|
+
* @description 通用 OSS 配置项 (不同的厂商的配置会在这个配置上扩展)
|
|
10
|
+
* @param vendor: OSSVendor OSS 厂商类型
|
|
11
|
+
* @param secure?: boolean 是否使用 HTTPS
|
|
12
|
+
* @param customDomain?: string 自定义域名
|
|
13
|
+
* */
|
|
14
|
+
interface OSSConfigBase {
|
|
15
|
+
vendor: OSSVendor;
|
|
16
|
+
secure?: boolean;
|
|
17
|
+
customDomain?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @description 阿里云 OSS 配置
|
|
21
|
+
* @param region: string OSS地域
|
|
22
|
+
* @param accessKeyId: string OSS AccessKeyId
|
|
23
|
+
* @param accessKeySecret: string OSS AccessKeySecret
|
|
24
|
+
* @param bucket: string OSS 存储空间名称
|
|
25
|
+
* @param endpoint?: string OSS 域名
|
|
26
|
+
* */
|
|
27
|
+
interface AliyunOSSConfig extends OSSConfigBase {
|
|
28
|
+
vendor: 'aliyun';
|
|
29
|
+
region: string;
|
|
30
|
+
accessKeyId: string;
|
|
31
|
+
accessKeySecret: string;
|
|
32
|
+
bucket: string;
|
|
33
|
+
endpoint?: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* @description 腾讯云 COS 配置
|
|
37
|
+
* @param region: string COS 地域
|
|
38
|
+
* @param secretId: string COS AccessKeyId
|
|
39
|
+
* @param secretKey: string COS AccessKeySecret
|
|
40
|
+
* @param bucket: string COS 存储空间名称
|
|
41
|
+
* @param appId: string 腾讯云 AppId
|
|
42
|
+
* */
|
|
43
|
+
interface TencentOSSConfig extends OSSConfigBase {
|
|
44
|
+
vendor: 'tencent';
|
|
45
|
+
region: string;
|
|
46
|
+
secretId: string;
|
|
47
|
+
secretKey: string;
|
|
48
|
+
bucket: string;
|
|
49
|
+
appId: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* @description 七牛云 OSS 配置
|
|
53
|
+
* @param zone: string 七牛云 OSS 地域( z0:华东, z1:华北, z2:华南, na0:北美, as0:东南亚)
|
|
54
|
+
* @param accessKey: string 七牛云 OSS AccessKeyId
|
|
55
|
+
* @param secretKey: string 七牛云 OSS SecretKey
|
|
56
|
+
* @param bucket: string 七牛云 OSS 存储空间名称
|
|
57
|
+
* @param domain: string 七牛云 OSS 域名
|
|
58
|
+
* */
|
|
59
|
+
interface QiniuOSSConfig extends OSSConfigBase {
|
|
60
|
+
vendor: 'qiniu';
|
|
61
|
+
zone: string;
|
|
62
|
+
accessKey: string;
|
|
63
|
+
secretKey: string;
|
|
64
|
+
bucket: string;
|
|
65
|
+
domain: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 联合类型:所有 OSS 配置
|
|
69
|
+
*/
|
|
70
|
+
type OSSConfig = AliyunOSSConfig | TencentOSSConfig | QiniuOSSConfig;
|
|
71
|
+
/**
|
|
72
|
+
* @description 上传的选项
|
|
73
|
+
* @param targetPath: string 上传到 OSS 的目标路径
|
|
74
|
+
* @param overwrite?: boolean 是否覆盖同名文件
|
|
75
|
+
* @param timeout?: number 上传超时时间
|
|
76
|
+
* @param partSize?: number 分片大小
|
|
77
|
+
* @param parallel?: number 分片上传的并发数
|
|
78
|
+
* @param onProgress?: (progress: number) => void 上传进度回调
|
|
79
|
+
**/
|
|
80
|
+
interface UploadOptions {
|
|
81
|
+
targetPath: string;
|
|
82
|
+
overwrite?: boolean;
|
|
83
|
+
timeout?: number;
|
|
84
|
+
partSize?: number;
|
|
85
|
+
parallel?: number;
|
|
86
|
+
onProgress?: (progress: number) => void;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* @description 上传结果
|
|
90
|
+
* @param url: string 上传后的 URL
|
|
91
|
+
* @param path: string 上传后的路径
|
|
92
|
+
* @param size: number 上传的文件大小
|
|
93
|
+
* @param costTime: number 上传耗时
|
|
94
|
+
* @param md5?: string 文件的 md5 值
|
|
95
|
+
*/
|
|
96
|
+
interface UploadResult {
|
|
97
|
+
url: string;
|
|
98
|
+
path: string;
|
|
99
|
+
size: number;
|
|
100
|
+
costTime: number;
|
|
101
|
+
md5?: string;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* @description OSS 错误类型
|
|
105
|
+
* */
|
|
106
|
+
declare class OSSUploadError extends Error {
|
|
107
|
+
/** 错误码 */
|
|
108
|
+
code: string;
|
|
109
|
+
/** 请求 ID(部分厂商提供) */
|
|
110
|
+
requestId?: string;
|
|
111
|
+
/** 厂商错误码 */
|
|
112
|
+
vendorCode?: string;
|
|
113
|
+
constructor(message: string, code: string, requestId?: string, vendorCode?: string);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* OSS 上传器抽象基类
|
|
118
|
+
* 定义所有厂商上传器必须实现的核心方法
|
|
119
|
+
*/
|
|
120
|
+
declare abstract class BaseOSSUploader {
|
|
121
|
+
protected config: OSSConfig;
|
|
122
|
+
protected client: any;
|
|
123
|
+
constructor(config: OSSConfig);
|
|
124
|
+
/** 验证配置合法性(子类实现) */
|
|
125
|
+
protected abstract validateConfig(config: OSSConfig): OSSConfig;
|
|
126
|
+
/** 初始化厂商客户端(子类实现) */
|
|
127
|
+
protected abstract initClient(): void;
|
|
128
|
+
/** 上传本地文件 */
|
|
129
|
+
abstract uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;
|
|
130
|
+
/** 删除文件 */
|
|
131
|
+
abstract deleteFile(objectName: string): Promise<boolean>;
|
|
132
|
+
/** 获取文件访问 URL */
|
|
133
|
+
abstract getFileUrl(objectName: string): string;
|
|
134
|
+
/**
|
|
135
|
+
* 检查客户端是否可用,destroy() 后调用会抛出 CLIENT_NOT_INIT
|
|
136
|
+
*/
|
|
137
|
+
protected checkClient(): void;
|
|
138
|
+
/**
|
|
139
|
+
* 校验本地文件路径合法性,返回文件 Stats
|
|
140
|
+
* 路径不存在或不是文件时抛出 NOT_A_FILE
|
|
141
|
+
*/
|
|
142
|
+
protected validateFile(filePath: string): Stats;
|
|
143
|
+
/**
|
|
144
|
+
* 销毁客户端,释放资源
|
|
145
|
+
*/
|
|
146
|
+
destroy(): void;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
declare class AliyunOSSUploader extends BaseOSSUploader {
|
|
150
|
+
protected config: AliyunOSSConfig;
|
|
151
|
+
protected validateConfig(config: AliyunOSSConfig): AliyunOSSConfig;
|
|
152
|
+
protected initClient(): void;
|
|
153
|
+
uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;
|
|
154
|
+
getFileUrl(objectName: string): string;
|
|
155
|
+
deleteFile(objectName: string): Promise<boolean>;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
declare class TencentOSSUploader extends BaseOSSUploader {
|
|
159
|
+
protected config: TencentOSSConfig;
|
|
160
|
+
protected validateConfig(config: OSSConfig): TencentOSSConfig;
|
|
161
|
+
protected initClient(): void;
|
|
162
|
+
/** 完整存储桶名称,格式:BucketName-AppId */
|
|
163
|
+
private get fullBucket();
|
|
164
|
+
uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;
|
|
165
|
+
getFileUrl(objectName: string): string;
|
|
166
|
+
deleteFile(objectName: string): Promise<boolean>;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
declare class QiniuOSSUploader extends BaseOSSUploader {
|
|
170
|
+
protected config: QiniuOSSConfig;
|
|
171
|
+
protected validateConfig(config: OSSConfig): QiniuOSSConfig;
|
|
172
|
+
protected initClient(): void;
|
|
173
|
+
private getUploadToken;
|
|
174
|
+
uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;
|
|
175
|
+
/**
|
|
176
|
+
* 检查文件是否存在:stat 成功则存在,以 612 拒绝则不存在,其余错误上抛
|
|
177
|
+
*/
|
|
178
|
+
private checkFileExists;
|
|
179
|
+
getFileUrl(objectName: string): string;
|
|
180
|
+
deleteFile(objectName: string): Promise<boolean>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 工厂函数:根据配置中的 vendor 字段自动创建对应厂商的 OSS 上传器
|
|
185
|
+
*/
|
|
186
|
+
declare function createOSSUploader(config: OSSConfig): BaseOSSUploader;
|
|
187
|
+
|
|
188
|
+
export { type AliyunOSSConfig, AliyunOSSUploader, BaseOSSUploader, type OSSConfig, type OSSConfigBase, OSSUploadError, type OSSVendor, type QiniuOSSConfig, QiniuOSSUploader, type TencentOSSConfig, TencentOSSUploader, type UploadOptions, type UploadResult, createOSSUploader };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var L=Object.create;var h=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var x=Object.getPrototypeOf,v=Object.prototype.hasOwnProperty;var z=(r,e)=>{for(var t in e)h(r,t,{get:e[t],enumerable:!0})},F=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of $(e))!v.call(r,s)&&s!==t&&h(r,s,{get:()=>e[s],enumerable:!(i=P(e,s))||i.enumerable});return r};var m=(r,e,t)=>(t=r!=null?L(x(r)):{},F(e||!r||!r.__esModule?h(t,"default",{value:r,enumerable:!0}):t,r)),A=r=>F(h({},"__esModule",{value:!0}),r);var K={};z(K,{AliyunOSSUploader:()=>g,BaseOSSUploader:()=>u,OSSUploadError:()=>n,QiniuOSSUploader:()=>p,TencentOSSUploader:()=>d,createOSSUploader:()=>N});module.exports=A(K);var b=m(require("ali-oss")),_=require("fs");var U=require("fs");var n=class extends Error{constructor(e,t,i,s){super(e),this.name="OSSUploadError",this.code=t,this.requestId=i,this.vendorCode=s}};var u=class{constructor(e){this.config=this.validateConfig(e),this.initClient()}checkClient(){if(!this.client)throw new n("\u5BA2\u6237\u7AEF\u672A\u521D\u59CB\u5316\u6216\u5DF2\u9500\u6BC1","CLIENT_NOT_INIT")}validateFile(e){let t=(0,U.statSync)(e);if(!t.isFile())throw new n("\u6307\u5B9A\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6","NOT_A_FILE");return t}destroy(){this.client=null}};var g=class extends u{validateConfig(e){let i=["region","accessKeyId","accessKeySecret","bucket"].filter(s=>!e[s]);if(i.length>0)throw new n(`\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5\uFF1A${i.join(", ")}`,"INVALID_CONFIG","MISSING_REQUIRED_FIELDS");return{secure:!0,...e}}initClient(){try{this.client=new b.default({region:this.config.region,accessKeyId:this.config.accessKeyId,accessKeySecret:this.config.accessKeySecret,bucket:this.config.bucket,endpoint:this.config.endpoint})}catch(e){throw new n(`\u521D\u59CB\u5316 OSS \u5BA2\u6237\u7AEF\u5931\u8D25: ${e.message}`,"CLIENT_INIT_FAILED",e.code,e.requestId)}}async uploadFile(e,t){this.checkClient();let i={overwrite:!1,timeout:6e4,partSize:10*1024*1024,parallel:5,...t},s=Date.now();try{let o=this.validateFile(e);if(!i.overwrite)try{throw await this.client.head(i.targetPath),new n(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${i.targetPath}`,"FILE_EXISTS","ALIYUN_FILE_EXISTS")}catch(l){if(l instanceof n)throw l;if(l.code!=="NoSuchKey")throw new n(`\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u5B58\u5728\u5931\u8D25: ${l.message}`,"CHECK_FILE_FAILED",l.code,l.requestId)}let f=(0,_.createReadStream)(e),S=await this.client.putStream(i.targetPath,f,{timeout:i.timeout,partSize:i.partSize,parallel:i.parallel,progress:l=>{i.onProgress?.(Math.floor(l*100))}}),c=Date.now()-s;return{url:this.getFileUrl(i.targetPath),path:i.targetPath,size:o.size,costTime:c,md5:S.res.headers["content-md5"]}}catch(o){throw o instanceof n?o:new n(`\u963F\u91CC\u4E91\u4E0A\u4F20\u5931\u8D25: ${o.message}`,"UPLOAD_FAILED",o.code,o.requestId)}}getFileUrl(e){return this.config.customDomain?`${this.config.secure?"https":"http"}://${this.config.customDomain}/${e}`:(this.checkClient(),this.client.signatureUrl(e,{expires:0}))}async deleteFile(e){this.checkClient();try{return await this.client.delete(e),!0}catch(t){if(t.code==="NoSuchKey")return!0;throw new n(`\u963F\u91CC\u4E91\u5220\u9664\u6587\u4EF6\u5931\u8D25: ${t.message}`,"DELETE_FAILED",t.code,t.requestId)}}};var k=m(require("cos-nodejs-sdk-v5")),T=require("fs");var d=class extends u{validateConfig(e){let t=e,s=["region","secretId","secretKey","bucket","appId"].filter(o=>!t[o]);if(s.length>0)throw new n(`\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5\uFF1A${s.join(", ")}`,"INVALID_CONFIG","MISSING_REQUIRED_FIELDS");return{secure:!0,...t}}initClient(){try{this.client=new k.default({SecretId:this.config.secretId,SecretKey:this.config.secretKey})}catch(e){throw new n(`\u521D\u59CB\u5316\u817E\u8BAF\u4E91 COS \u5BA2\u6237\u7AEF\u5931\u8D25: ${e.message}`,"CLIENT_INIT_FAILED",e.code)}}get fullBucket(){return`${this.config.bucket}-${this.config.appId}`}async uploadFile(e,t){this.checkClient();let i={overwrite:!1,timeout:6e4,partSize:10*1024*1024,parallel:5,...t},s=Date.now();try{let o=this.validateFile(e);if(!i.overwrite)try{throw await this.client.headObject({Bucket:this.fullBucket,Region:this.config.region,Key:i.targetPath}),new n(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${i.targetPath}`,"FILE_EXISTS","TENCENT_FILE_EXISTS")}catch(c){if(c instanceof n)throw c;if(c.statusCode!==404)throw new n(`\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u5B58\u5728\u5931\u8D25: ${c.message}`,"CHECK_FILE_FAILED",c.code)}let f=await this.client.putObject({Bucket:this.fullBucket,Region:this.config.region,Key:i.targetPath,Body:(0,T.createReadStream)(e),ContentLength:o.size,onProgress:c=>{i.onProgress?.(Math.floor(c.percent*100))}}),S=Date.now()-s;return{url:this.getFileUrl(i.targetPath),path:i.targetPath,size:o.size,costTime:S,md5:f?.ETag?.replace(/"/g,"")}}catch(o){throw o instanceof n?o:new n(`\u817E\u8BAF\u4E91\u4E0A\u4F20\u5931\u8D25: ${o.message}`,"UPLOAD_FAILED",o.code)}}getFileUrl(e){return this.config.customDomain?`${this.config.secure?"https":"http"}://${this.config.customDomain}/${e}`:`${this.config.secure?"https":"http"}://${this.fullBucket}.cos.${this.config.region}.myqcloud.com/${e}`}async deleteFile(e){this.checkClient();try{return await this.client.deleteObject({Bucket:this.fullBucket,Region:this.config.region,Key:e}),!0}catch(t){if(t.statusCode===404)return!0;throw new n(`\u817E\u8BAF\u4E91\u5220\u9664\u6587\u4EF6\u5931\u8D25: ${t.message}`,"DELETE_FAILED",t.code)}}};var a=m(require("qiniu"));var C={z0:a.default.zone.Zone_z0,z1:a.default.zone.Zone_z1,z2:a.default.zone.Zone_z2,na0:a.default.zone.Zone_na0,as0:a.default.zone.Zone_as0},p=class extends u{validateConfig(e){let t=e,s=["zone","accessKey","secretKey","bucket","domain"].filter(o=>!t[o]);if(s.length>0)throw new n(`\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5\uFF1A${s.join(", ")}`,"INVALID_CONFIG","MISSING_REQUIRED_FIELDS");if(!C[t.zone])throw new n(`\u65E0\u6548\u7684 zone \u503C: ${t.zone}\uFF0C\u652F\u6301\u7684\u503C\u4E3A: ${Object.keys(C).join(", ")}`,"INVALID_CONFIG","INVALID_ZONE");return{secure:!0,...t}}initClient(){try{let e=new a.default.auth.digest.Mac(this.config.accessKey,this.config.secretKey),t=new a.default.conf.Config;t.zone=C[this.config.zone],this.client={mac:e,formUploader:new a.default.form_up.FormUploader(t),bucketManager:new a.default.rs.BucketManager(e,t)}}catch(e){throw new n(`\u521D\u59CB\u5316\u4E03\u725B\u4E91\u5BA2\u6237\u7AEF\u5931\u8D25: ${e.message}`,"CLIENT_INIT_FAILED",e.code)}}getUploadToken(e){let t=this.client;return new a.default.rs.PutPolicy({scope:`${this.config.bucket}:${e}`}).uploadToken(t.mac)}async uploadFile(e,t){this.checkClient();let i={overwrite:!1,timeout:6e4,...t},s=Date.now();try{let o=this.validateFile(e);if(!i.overwrite&&await this.checkFileExists(i.targetPath))throw new n(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${i.targetPath}`,"FILE_EXISTS","QINIU_FILE_EXISTS");let f=this.getUploadToken(i.targetPath),S=new a.default.form_up.PutExtra,c=this.client,l=await new Promise((O,y)=>{c.formUploader.putFile(f,i.targetPath,e,S,(w,I,E)=>{if(w){y(w);return}E.statusCode===200?O(I):y(new Error(`\u4E0A\u4F20\u5931\u8D25\uFF0C\u72B6\u6001\u7801: ${E.statusCode}, ${JSON.stringify(I)}`))})}),D=Date.now()-s;return{url:this.getFileUrl(i.targetPath),path:i.targetPath,size:o.size,costTime:D,md5:l?.hash}}catch(o){throw o instanceof n?o:new n(`\u4E03\u725B\u4E91\u4E0A\u4F20\u5931\u8D25: ${o.message}`,"UPLOAD_FAILED",o.code)}}async checkFileExists(e){let t=this.client;try{return await t.bucketManager.stat(this.config.bucket,e),!0}catch(i){if(i?.statusCode===612)return!1;throw i}}getFileUrl(e){let t=this.config.customDomain||this.config.domain;return`${this.config.secure?"https":"http"}://${t}/${e}`}async deleteFile(e){this.checkClient();try{return await this.client.bucketManager.delete(this.config.bucket,e),!0}catch(t){if(t?.statusCode===612)return!0;throw new n(`\u4E03\u725B\u4E91\u5220\u9664\u6587\u4EF6\u5931\u8D25: ${t.message}`,"DELETE_FAILED",t.code)}}};function N(r){switch(r.vendor){case"aliyun":return new g(r);case"tencent":return new d(r);case"qiniu":return new p(r);default:throw new Error(`\u4E0D\u652F\u6301\u7684 OSS \u5382\u5546: ${r.vendor}`)}}0&&(module.exports={AliyunOSSUploader,BaseOSSUploader,OSSUploadError,QiniuOSSUploader,TencentOSSUploader,createOSSUploader});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapters/AliyunOSSUploader.ts","../src/core/BaseOSSUploader.ts","../src/types/index.ts","../src/adapters/TencentOSSUploader.ts","../src/adapters/QiniuOSSUploader.ts"],"sourcesContent":["export { AliyunOSSUploader } from './adapters/AliyunOSSUploader.ts';\r\nexport { TencentOSSUploader } from './adapters/TencentOSSUploader.ts';\r\nexport { QiniuOSSUploader } from './adapters/QiniuOSSUploader.ts';\r\nexport { BaseOSSUploader } from './core/BaseOSSUploader.ts';\r\nexport type {\r\n OSSVendor,\r\n OSSConfigBase,\r\n AliyunOSSConfig,\r\n TencentOSSConfig,\r\n QiniuOSSConfig,\r\n OSSConfig,\r\n UploadOptions,\r\n UploadResult\r\n} from './types/index.ts';\r\nexport { OSSUploadError } from './types/index.ts';\r\n\r\nimport { AliyunOSSUploader } from './adapters/AliyunOSSUploader.ts';\r\nimport { TencentOSSUploader } from './adapters/TencentOSSUploader.ts';\r\nimport { QiniuOSSUploader } from './adapters/QiniuOSSUploader.ts';\r\nimport { BaseOSSUploader } from './core/BaseOSSUploader.ts';\r\nimport type { OSSConfig } from './types/index.ts';\r\n\r\n/**\r\n * 工厂函数:根据配置中的 vendor 字段自动创建对应厂商的 OSS 上传器\r\n */\r\nexport function createOSSUploader(config: OSSConfig): BaseOSSUploader {\r\n switch (config.vendor) {\r\n case 'aliyun':\r\n return new AliyunOSSUploader(config);\r\n case 'tencent':\r\n return new TencentOSSUploader(config);\r\n case 'qiniu':\r\n return new QiniuOSSUploader(config);\r\n default:\r\n throw new Error(`不支持的 OSS 厂商: ${(config as any).vendor}`);\r\n }\r\n}\r\n","import OSS from \"ali-oss\";\r\nimport { createReadStream } from \"fs\";\r\nimport { BaseOSSUploader } from \"../core/BaseOSSUploader.ts\";\r\nimport {\r\n AliyunOSSConfig,\r\n OSSUploadError,\r\n UploadOptions,\r\n UploadResult\r\n} from \"../types/index.ts\";\r\n\r\nexport class AliyunOSSUploader extends BaseOSSUploader {\r\n declare protected config: AliyunOSSConfig;\r\n\r\n protected validateConfig(config: AliyunOSSConfig): AliyunOSSConfig {\r\n const requiredFields = ['region', 'accessKeyId', 'accessKeySecret', 'bucket'];\r\n const missingFields = requiredFields.filter(\r\n field => !config[field as keyof AliyunOSSConfig]\r\n );\r\n if (missingFields.length > 0) {\r\n throw new OSSUploadError(\r\n `缺少必要字段:${missingFields.join(', ')}`,\r\n 'INVALID_CONFIG',\r\n 'MISSING_REQUIRED_FIELDS'\r\n );\r\n }\r\n return { secure: true, ...config };\r\n }\r\n\r\n protected initClient(): void {\r\n try {\r\n this.client = new OSS({\r\n region: this.config.region,\r\n accessKeyId: this.config.accessKeyId,\r\n accessKeySecret: this.config.accessKeySecret,\r\n bucket: this.config.bucket,\r\n endpoint: this.config.endpoint\r\n });\r\n } catch (error: any) {\r\n throw new OSSUploadError(\r\n `初始化 OSS 客户端失败: ${error.message}`,\r\n 'CLIENT_INIT_FAILED',\r\n error.code,\r\n error.requestId\r\n );\r\n }\r\n }\r\n\r\n public async uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult> {\r\n this.checkClient();\r\n\r\n const uploadOptions = {\r\n overwrite: false,\r\n timeout: 60000,\r\n partSize: 10 * 1024 * 1024,\r\n parallel: 5,\r\n ...options\r\n };\r\n\r\n const startTime = Date.now();\r\n\r\n try {\r\n const fileStat = this.validateFile(filePath);\r\n\r\n // 检查文件是否存在\r\n if (!uploadOptions.overwrite) {\r\n try {\r\n await this.client.head(uploadOptions.targetPath);\r\n throw new OSSUploadError(\r\n `文件已存在: ${uploadOptions.targetPath}`,\r\n 'FILE_EXISTS',\r\n 'ALIYUN_FILE_EXISTS'\r\n );\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n if (error.code !== 'NoSuchKey') {\r\n throw new OSSUploadError(\r\n `检查文件是否存在失败: ${error.message}`,\r\n 'CHECK_FILE_FAILED',\r\n error.code,\r\n error.requestId\r\n );\r\n }\r\n }\r\n }\r\n\r\n // 执行上传\r\n const fileStream = createReadStream(filePath);\r\n const result = await this.client.putStream(\r\n uploadOptions.targetPath,\r\n fileStream,\r\n {\r\n timeout: uploadOptions.timeout,\r\n partSize: uploadOptions.partSize,\r\n parallel: uploadOptions.parallel,\r\n progress: (p: number) => {\r\n uploadOptions.onProgress?.(Math.floor(p * 100));\r\n }\r\n }\r\n );\r\n\r\n const costTime = Date.now() - startTime;\r\n return {\r\n url: this.getFileUrl(uploadOptions.targetPath),\r\n path: uploadOptions.targetPath,\r\n size: fileStat.size,\r\n costTime,\r\n md5: result.res.headers['content-md5']\r\n };\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n throw new OSSUploadError(\r\n `阿里云上传失败: ${error.message}`,\r\n 'UPLOAD_FAILED',\r\n error.code,\r\n error.requestId\r\n );\r\n }\r\n }\r\n\r\n public getFileUrl(objectName: string): string {\r\n // 优先使用自定义域名,无需客户端\r\n if (this.config.customDomain) {\r\n return `${this.config.secure ? 'https' : 'http'}://${this.config.customDomain}/${objectName}`;\r\n }\r\n // signatureUrl 需要客户端\r\n this.checkClient();\r\n return this.client.signatureUrl(objectName, { expires: 0 });\r\n }\r\n\r\n public async deleteFile(objectName: string): Promise<boolean> {\r\n this.checkClient();\r\n\r\n try {\r\n await this.client.delete(objectName);\r\n return true;\r\n } catch (error: any) {\r\n if (error.code === 'NoSuchKey') return true;\r\n throw new OSSUploadError(\r\n `阿里云删除文件失败: ${error.message}`,\r\n 'DELETE_FAILED',\r\n error.code,\r\n error.requestId\r\n );\r\n }\r\n }\r\n}\r\n","import { statSync, Stats } from 'fs';\r\nimport { OSSConfig, OSSUploadError, UploadOptions, UploadResult } from \"../types/index.ts\";\r\n\r\n/**\r\n * OSS 上传器抽象基类\r\n * 定义所有厂商上传器必须实现的核心方法\r\n */\r\nexport abstract class BaseOSSUploader {\r\n protected config: OSSConfig;\r\n protected client: any; // 具体厂商的 SDK 客户端实例\r\n\r\n constructor(config: OSSConfig) {\r\n this.config = this.validateConfig(config);\r\n this.initClient();\r\n }\r\n\r\n /** 验证配置合法性(子类实现) */\r\n protected abstract validateConfig(config: OSSConfig): OSSConfig;\r\n /** 初始化厂商客户端(子类实现) */\r\n protected abstract initClient(): void;\r\n /** 上传本地文件 */\r\n public abstract uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;\r\n /** 删除文件 */\r\n public abstract deleteFile(objectName: string): Promise<boolean>;\r\n /** 获取文件访问 URL */\r\n public abstract getFileUrl(objectName: string): string;\r\n\r\n /**\r\n * 检查客户端是否可用,destroy() 后调用会抛出 CLIENT_NOT_INIT\r\n */\r\n protected checkClient(): void {\r\n if (!this.client) {\r\n throw new OSSUploadError('客户端未初始化或已销毁', 'CLIENT_NOT_INIT');\r\n }\r\n }\r\n\r\n /**\r\n * 校验本地文件路径合法性,返回文件 Stats\r\n * 路径不存在或不是文件时抛出 NOT_A_FILE\r\n */\r\n protected validateFile(filePath: string): Stats {\r\n const fileStat = statSync(filePath);\r\n if (!fileStat.isFile()) {\r\n throw new OSSUploadError('指定路径不是文件', 'NOT_A_FILE');\r\n }\r\n return fileStat;\r\n }\r\n\r\n /**\r\n * 销毁客户端,释放资源\r\n */\r\n public destroy(): void {\r\n this.client = null;\r\n }\r\n}\r\n","/**\r\n * @description 支持的 OSS 的厂商类型\r\n *\r\n * */\r\nexport type OSSVendor = 'aliyun' | 'tencent' | 'qiniu'\r\n\r\n/**\r\n * @description 通用 OSS 配置项 (不同的厂商的配置会在这个配置上扩展)\r\n * @param vendor: OSSVendor OSS 厂商类型\r\n * @param secure?: boolean 是否使用 HTTPS\r\n * @param customDomain?: string 自定义域名\r\n * */\r\nexport interface OSSConfigBase {\r\n vendor: OSSVendor;\r\n secure?: boolean;\r\n customDomain?: string;\r\n}\r\n\r\n\r\n/**\r\n * @description 阿里云 OSS 配置\r\n * @param region: string OSS地域\r\n * @param accessKeyId: string OSS AccessKeyId\r\n * @param accessKeySecret: string OSS AccessKeySecret\r\n * @param bucket: string OSS 存储空间名称\r\n * @param endpoint?: string OSS 域名\r\n * */\r\nexport interface AliyunOSSConfig extends OSSConfigBase{\r\n vendor: 'aliyun';\r\n region: string;\r\n accessKeyId: string;\r\n accessKeySecret: string;\r\n bucket: string;\r\n endpoint?: string;\r\n}\r\n/**\r\n * @description 腾讯云 COS 配置\r\n * @param region: string COS 地域\r\n * @param secretId: string COS AccessKeyId\r\n * @param secretKey: string COS AccessKeySecret\r\n * @param bucket: string COS 存储空间名称\r\n * @param appId: string 腾讯云 AppId\r\n * */\r\nexport interface TencentOSSConfig extends OSSConfigBase{\r\n vendor: 'tencent';\r\n region: string;\r\n secretId: string;\r\n secretKey: string;\r\n bucket: string;\r\n appId: string;\r\n}\r\n/**\r\n * @description 七牛云 OSS 配置\r\n * @param zone: string 七牛云 OSS 地域( z0:华东, z1:华北, z2:华南, na0:北美, as0:东南亚)\r\n * @param accessKey: string 七牛云 OSS AccessKeyId\r\n * @param secretKey: string 七牛云 OSS SecretKey\r\n * @param bucket: string 七牛云 OSS 存储空间名称\r\n * @param domain: string 七牛云 OSS 域名\r\n * */\r\nexport interface QiniuOSSConfig extends OSSConfigBase{\r\n vendor: 'qiniu';\r\n zone: string;\r\n accessKey: string;\r\n secretKey: string;\r\n bucket: string;\r\n domain: string;\r\n}\r\n/**\r\n * 联合类型:所有 OSS 配置\r\n */\r\nexport type OSSConfig = AliyunOSSConfig | TencentOSSConfig | QiniuOSSConfig;\r\n\r\n/**\r\n * @description 上传的选项\r\n * @param targetPath: string 上传到 OSS 的目标路径\r\n * @param overwrite?: boolean 是否覆盖同名文件\r\n * @param timeout?: number 上传超时时间\r\n * @param partSize?: number 分片大小\r\n * @param parallel?: number 分片上传的并发数\r\n * @param onProgress?: (progress: number) => void 上传进度回调\r\n**/\r\nexport interface UploadOptions {\r\n targetPath: string;\r\n overwrite?: boolean;\r\n timeout?: number;\r\n partSize?: number;\r\n parallel?: number;\r\n onProgress?: (progress: number) => void;\r\n}\r\n\r\n/**\r\n * @description 上传结果\r\n * @param url: string 上传后的 URL\r\n * @param path: string 上传后的路径\r\n * @param size: number 上传的文件大小\r\n * @param costTime: number 上传耗时\r\n * @param md5?: string 文件的 md5 值\r\n */\r\nexport interface UploadResult {\r\n url: string;\r\n path: string;\r\n size: number;\r\n costTime: number;\r\n md5?: string;\r\n}\r\n/**\r\n * @description OSS 错误类型\r\n * */\r\nexport class OSSUploadError extends Error {\r\n /** 错误码 */\r\n code: string;\r\n /** 请求 ID(部分厂商提供) */\r\n requestId?: string;\r\n /** 厂商错误码 */\r\n vendorCode?: string;\r\n constructor(message: string, code: string, requestId?: string,vendorCode?: string) {\r\n super(message);\r\n this.name = 'OSSUploadError';\r\n this.code = code;\r\n this.requestId = requestId;\r\n this.vendorCode = vendorCode;\r\n }\r\n}","import COS from 'cos-nodejs-sdk-v5';\r\nimport { createReadStream } from 'fs';\r\nimport { BaseOSSUploader } from '../core/BaseOSSUploader.ts';\r\nimport {\r\n TencentOSSConfig,\r\n OSSConfig,\r\n OSSUploadError,\r\n UploadOptions,\r\n UploadResult\r\n} from '../types/index.ts';\r\n\r\nexport class TencentOSSUploader extends BaseOSSUploader {\r\n declare protected config: TencentOSSConfig;\r\n\r\n protected validateConfig(config: OSSConfig): TencentOSSConfig {\r\n const tencentConfig = config as TencentOSSConfig;\r\n const requiredFields = ['region', 'secretId', 'secretKey', 'bucket', 'appId'];\r\n const missingFields = requiredFields.filter(\r\n field => !tencentConfig[field as keyof TencentOSSConfig]\r\n );\r\n if (missingFields.length > 0) {\r\n throw new OSSUploadError(\r\n `缺少必要字段:${missingFields.join(', ')}`,\r\n 'INVALID_CONFIG',\r\n 'MISSING_REQUIRED_FIELDS'\r\n );\r\n }\r\n return { secure: true, ...tencentConfig };\r\n }\r\n\r\n protected initClient(): void {\r\n try {\r\n this.client = new COS({\r\n SecretId: this.config.secretId,\r\n SecretKey: this.config.secretKey,\r\n });\r\n } catch (error: any) {\r\n throw new OSSUploadError(\r\n `初始化腾讯云 COS 客户端失败: ${error.message}`,\r\n 'CLIENT_INIT_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n\r\n /** 完整存储桶名称,格式:BucketName-AppId */\r\n private get fullBucket(): string {\r\n return `${this.config.bucket}-${this.config.appId}`;\r\n }\r\n\r\n public async uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult> {\r\n this.checkClient();\r\n\r\n const uploadOptions = {\r\n overwrite: false,\r\n timeout: 60000,\r\n partSize: 10 * 1024 * 1024,\r\n parallel: 5,\r\n ...options\r\n };\r\n\r\n const startTime = Date.now();\r\n\r\n try {\r\n const fileStat = this.validateFile(filePath);\r\n\r\n // 检查文件是否存在\r\n if (!uploadOptions.overwrite) {\r\n try {\r\n await this.client.headObject({\r\n Bucket: this.fullBucket,\r\n Region: this.config.region,\r\n Key: uploadOptions.targetPath,\r\n });\r\n throw new OSSUploadError(\r\n `文件已存在: ${uploadOptions.targetPath}`,\r\n 'FILE_EXISTS',\r\n 'TENCENT_FILE_EXISTS'\r\n );\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n // statusCode 404 表示文件不存在,可以继续上传\r\n if (error.statusCode !== 404) {\r\n throw new OSSUploadError(\r\n `检查文件是否存在失败: ${error.message}`,\r\n 'CHECK_FILE_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n }\r\n\r\n // 执行上传\r\n const result = await this.client.putObject({\r\n Bucket: this.fullBucket,\r\n Region: this.config.region,\r\n Key: uploadOptions.targetPath,\r\n Body: createReadStream(filePath),\r\n ContentLength: fileStat.size,\r\n onProgress: (params: COS.ProgressInfo) => {\r\n uploadOptions.onProgress?.(Math.floor(params.percent * 100));\r\n },\r\n });\r\n\r\n const costTime = Date.now() - startTime;\r\n return {\r\n url: this.getFileUrl(uploadOptions.targetPath),\r\n path: uploadOptions.targetPath,\r\n size: fileStat.size,\r\n costTime,\r\n md5: result?.ETag?.replace(/\"/g, '')\r\n };\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n throw new OSSUploadError(\r\n `腾讯云上传失败: ${error.message}`,\r\n 'UPLOAD_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n\r\n public getFileUrl(objectName: string): string {\r\n if (this.config.customDomain) {\r\n return `${this.config.secure ? 'https' : 'http'}://${this.config.customDomain}/${objectName}`;\r\n }\r\n const protocol = this.config.secure ? 'https' : 'http';\r\n return `${protocol}://${this.fullBucket}.cos.${this.config.region}.myqcloud.com/${objectName}`;\r\n }\r\n\r\n public async deleteFile(objectName: string): Promise<boolean> {\r\n this.checkClient();\r\n\r\n try {\r\n await this.client.deleteObject({\r\n Bucket: this.fullBucket,\r\n Region: this.config.region,\r\n Key: objectName,\r\n });\r\n return true;\r\n } catch (error: any) {\r\n // 404 视为已删除\r\n if (error.statusCode === 404) return true;\r\n throw new OSSUploadError(\r\n `腾讯云删除文件失败: ${error.message}`,\r\n 'DELETE_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n}\r\n","import qiniu from 'qiniu';\r\nimport { BaseOSSUploader } from '../core/BaseOSSUploader.ts';\r\nimport {\r\n QiniuOSSConfig,\r\n OSSConfig,\r\n OSSUploadError,\r\n UploadOptions,\r\n UploadResult\r\n} from '../types/index.ts';\r\n\r\nconst ZONE_MAP: Record<string, qiniu.conf.Zone> = {\r\n z0: qiniu.zone.Zone_z0,\r\n z1: qiniu.zone.Zone_z1,\r\n z2: qiniu.zone.Zone_z2,\r\n na0: qiniu.zone.Zone_na0,\r\n as0: qiniu.zone.Zone_as0,\r\n};\r\n\r\ninterface QiniuClient {\r\n mac: qiniu.auth.digest.Mac;\r\n formUploader: qiniu.form_up.FormUploader;\r\n bucketManager: qiniu.rs.BucketManager;\r\n}\r\n\r\nexport class QiniuOSSUploader extends BaseOSSUploader {\r\n declare protected config: QiniuOSSConfig;\r\n\r\n protected validateConfig(config: OSSConfig): QiniuOSSConfig {\r\n const qiniuConfig = config as QiniuOSSConfig;\r\n const requiredFields = ['zone', 'accessKey', 'secretKey', 'bucket', 'domain'];\r\n const missingFields = requiredFields.filter(\r\n field => !qiniuConfig[field as keyof QiniuOSSConfig]\r\n );\r\n if (missingFields.length > 0) {\r\n throw new OSSUploadError(\r\n `缺少必要字段:${missingFields.join(', ')}`,\r\n 'INVALID_CONFIG',\r\n 'MISSING_REQUIRED_FIELDS'\r\n );\r\n }\r\n if (!ZONE_MAP[qiniuConfig.zone]) {\r\n throw new OSSUploadError(\r\n `无效的 zone 值: ${qiniuConfig.zone},支持的值为: ${Object.keys(ZONE_MAP).join(', ')}`,\r\n 'INVALID_CONFIG',\r\n 'INVALID_ZONE'\r\n );\r\n }\r\n return { secure: true, ...qiniuConfig };\r\n }\r\n\r\n protected initClient(): void {\r\n try {\r\n const mac = new qiniu.auth.digest.Mac(this.config.accessKey, this.config.secretKey);\r\n const cosConfig = new qiniu.conf.Config();\r\n cosConfig.zone = ZONE_MAP[this.config.zone];\r\n this.client = {\r\n mac,\r\n formUploader: new qiniu.form_up.FormUploader(cosConfig),\r\n bucketManager: new qiniu.rs.BucketManager(mac, cosConfig),\r\n } as QiniuClient;\r\n } catch (error: any) {\r\n throw new OSSUploadError(\r\n `初始化七牛云客户端失败: ${error.message}`,\r\n 'CLIENT_INIT_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n\r\n private getUploadToken(key: string): string {\r\n const client = this.client as QiniuClient;\r\n const putPolicy = new qiniu.rs.PutPolicy({\r\n scope: `${this.config.bucket}:${key}`\r\n });\r\n return putPolicy.uploadToken(client.mac);\r\n }\r\n\r\n public async uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult> {\r\n this.checkClient();\r\n\r\n const uploadOptions = {\r\n overwrite: false,\r\n timeout: 60000,\r\n ...options\r\n };\r\n\r\n const startTime = Date.now();\r\n\r\n try {\r\n const fileStat = this.validateFile(filePath);\r\n\r\n // 检查文件是否存在\r\n if (!uploadOptions.overwrite) {\r\n const exists = await this.checkFileExists(uploadOptions.targetPath);\r\n if (exists) {\r\n throw new OSSUploadError(\r\n `文件已存在: ${uploadOptions.targetPath}`,\r\n 'FILE_EXISTS',\r\n 'QINIU_FILE_EXISTS'\r\n );\r\n }\r\n }\r\n\r\n const uploadToken = this.getUploadToken(uploadOptions.targetPath);\r\n const putExtra = new qiniu.form_up.PutExtra();\r\n const client = this.client as QiniuClient;\r\n\r\n const result = await new Promise<{ hash: string }>((resolve, reject) => {\r\n client.formUploader.putFile(\r\n uploadToken,\r\n uploadOptions.targetPath,\r\n filePath,\r\n putExtra,\r\n (err: Error | undefined, respBody: any, respInfo: any) => {\r\n if (err) { reject(err); return; }\r\n if (respInfo.statusCode === 200) {\r\n resolve(respBody);\r\n } else {\r\n reject(new Error(`上传失败,状态码: ${respInfo.statusCode}, ${JSON.stringify(respBody)}`));\r\n }\r\n }\r\n );\r\n });\r\n\r\n const costTime = Date.now() - startTime;\r\n return {\r\n url: this.getFileUrl(uploadOptions.targetPath),\r\n path: uploadOptions.targetPath,\r\n size: fileStat.size,\r\n costTime,\r\n md5: result?.hash\r\n };\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n throw new OSSUploadError(\r\n `七牛云上传失败: ${error.message}`,\r\n 'UPLOAD_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * 检查文件是否存在:stat 成功则存在,以 612 拒绝则不存在,其余错误上抛\r\n */\r\n private async checkFileExists(key: string): Promise<boolean> {\r\n const client = this.client as QiniuClient;\r\n try {\r\n await client.bucketManager.stat(this.config.bucket, key);\r\n return true;\r\n } catch (error: any) {\r\n if (error?.statusCode === 612) return false;\r\n throw error;\r\n }\r\n }\r\n\r\n public getFileUrl(objectName: string): string {\r\n const domain = this.config.customDomain || this.config.domain;\r\n const protocol = this.config.secure ? 'https' : 'http';\r\n return `${protocol}://${domain}/${objectName}`;\r\n }\r\n\r\n public async deleteFile(objectName: string): Promise<boolean> {\r\n this.checkClient();\r\n\r\n try {\r\n const client = this.client as QiniuClient;\r\n await client.bucketManager.delete(this.config.bucket, objectName);\r\n return true;\r\n } catch (error: any) {\r\n // 612 表示文件不存在,也视为删除成功\r\n if (error?.statusCode === 612) return true;\r\n throw new OSSUploadError(\r\n `七牛云删除文件失败: ${error.message}`,\r\n 'DELETE_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n}\r\n"],"mappings":"0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,uBAAAE,EAAA,oBAAAC,EAAA,mBAAAC,EAAA,qBAAAC,EAAA,uBAAAC,EAAA,sBAAAC,IAAA,eAAAC,EAAAR,GCAA,IAAAS,EAAgB,sBAChBC,EAAiC,cCDjC,IAAAC,EAAgC,cC4GzB,IAAMC,EAAN,cAA6B,KAAM,CAOtC,YAAYC,EAAiBC,EAAcC,EAAmBC,EAAqB,CAC/E,MAAMH,CAAO,EACb,KAAK,KAAO,iBACZ,KAAK,KAAOC,EACZ,KAAK,UAAYC,EACjB,KAAK,WAAaC,CACtB,CACJ,EDnHO,IAAeC,EAAf,KAA+B,CAIlC,YAAYC,EAAmB,CAC3B,KAAK,OAAS,KAAK,eAAeA,CAAM,EACxC,KAAK,WAAW,CACpB,CAgBU,aAAoB,CAC1B,GAAI,CAAC,KAAK,OACN,MAAM,IAAIC,EAAe,qEAAe,iBAAiB,CAEjE,CAMU,aAAaC,EAAyB,CAC5C,IAAMC,KAAW,YAASD,CAAQ,EAClC,GAAI,CAACC,EAAS,OAAO,EACjB,MAAM,IAAIF,EAAe,mDAAY,YAAY,EAErD,OAAOE,CACX,CAKO,SAAgB,CACnB,KAAK,OAAS,IAClB,CACJ,ED5CO,IAAMC,EAAN,cAAgCC,CAAgB,CAGzC,eAAeC,EAA0C,CAE/D,IAAMC,EADiB,CAAC,SAAU,cAAe,kBAAmB,QAAQ,EACvC,OACjCC,GAAS,CAACF,EAAOE,CAA8B,CACnD,EACA,GAAID,EAAc,OAAS,EACvB,MAAM,IAAIE,EACN,6CAAUF,EAAc,KAAK,IAAI,CAAC,GAClC,iBACA,yBACJ,EAEJ,MAAO,CAAE,OAAQ,GAAM,GAAGD,CAAO,CACrC,CAEU,YAAmB,CACzB,GAAI,CACA,KAAK,OAAS,IAAI,EAAAI,QAAI,CAClB,OAAQ,KAAK,OAAO,OACpB,YAAa,KAAK,OAAO,YACzB,gBAAiB,KAAK,OAAO,gBAC7B,OAAQ,KAAK,OAAO,OACpB,SAAU,KAAK,OAAO,QAC1B,CAAC,CACL,OAASC,EAAY,CACjB,MAAM,IAAIF,EACN,0DAAkBE,EAAM,OAAO,GAC/B,qBACAA,EAAM,KACNA,EAAM,SACV,CACJ,CACJ,CAEA,MAAa,WAAWC,EAAkBC,EAA+C,CACrF,KAAK,YAAY,EAEjB,IAAMC,EAAgB,CAClB,UAAW,GACX,QAAS,IACT,SAAU,GAAK,KAAO,KACtB,SAAU,EACV,GAAGD,CACP,EAEME,EAAY,KAAK,IAAI,EAE3B,GAAI,CACA,IAAMC,EAAW,KAAK,aAAaJ,CAAQ,EAG3C,GAAI,CAACE,EAAc,UACf,GAAI,CACA,YAAM,KAAK,OAAO,KAAKA,EAAc,UAAU,EACzC,IAAIL,EACN,mCAAUK,EAAc,UAAU,GAClC,cACA,oBACJ,CACJ,OAASH,EAAY,CACjB,GAAIA,aAAiBF,EAAgB,MAAME,EAC3C,GAAIA,EAAM,OAAS,YACf,MAAM,IAAIF,EACN,iEAAeE,EAAM,OAAO,GAC5B,oBACAA,EAAM,KACNA,EAAM,SACV,CAER,CAIJ,IAAMM,KAAa,oBAAiBL,CAAQ,EACtCM,EAAS,MAAM,KAAK,OAAO,UAC7BJ,EAAc,WACdG,EACA,CACI,QAASH,EAAc,QACvB,SAAUA,EAAc,SACxB,SAAUA,EAAc,SACxB,SAAWK,GAAc,CACrBL,EAAc,aAAa,KAAK,MAAMK,EAAI,GAAG,CAAC,CAClD,CACJ,CACJ,EAEMC,EAAW,KAAK,IAAI,EAAIL,EAC9B,MAAO,CACH,IAAK,KAAK,WAAWD,EAAc,UAAU,EAC7C,KAAMA,EAAc,WACpB,KAAME,EAAS,KACf,SAAAI,EACA,IAAKF,EAAO,IAAI,QAAQ,aAAa,CACzC,CACJ,OAASP,EAAY,CACjB,MAAIA,aAAiBF,EAAsBE,EACrC,IAAIF,EACN,+CAAYE,EAAM,OAAO,GACzB,gBACAA,EAAM,KACNA,EAAM,SACV,CACJ,CACJ,CAEO,WAAWU,EAA4B,CAE1C,OAAI,KAAK,OAAO,aACL,GAAG,KAAK,OAAO,OAAS,QAAU,MAAM,MAAM,KAAK,OAAO,YAAY,IAAIA,CAAU,IAG/F,KAAK,YAAY,EACV,KAAK,OAAO,aAAaA,EAAY,CAAE,QAAS,CAAE,CAAC,EAC9D,CAEA,MAAa,WAAWA,EAAsC,CAC1D,KAAK,YAAY,EAEjB,GAAI,CACA,aAAM,KAAK,OAAO,OAAOA,CAAU,EAC5B,EACX,OAASV,EAAY,CACjB,GAAIA,EAAM,OAAS,YAAa,MAAO,GACvC,MAAM,IAAIF,EACN,2DAAcE,EAAM,OAAO,GAC3B,gBACAA,EAAM,KACNA,EAAM,SACV,CACJ,CACJ,CACJ,EGjJA,IAAAW,EAAgB,gCAChBC,EAAiC,cAU1B,IAAMC,EAAN,cAAiCC,CAAgB,CAG1C,eAAeC,EAAqC,CAC1D,IAAMC,EAAgBD,EAEhBE,EADiB,CAAC,SAAU,WAAY,YAAa,SAAU,OAAO,EACvC,OACjCC,GAAS,CAACF,EAAcE,CAA+B,CAC3D,EACA,GAAID,EAAc,OAAS,EACvB,MAAM,IAAIE,EACN,6CAAUF,EAAc,KAAK,IAAI,CAAC,GAClC,iBACA,yBACJ,EAEJ,MAAO,CAAE,OAAQ,GAAM,GAAGD,CAAc,CAC5C,CAEU,YAAmB,CACzB,GAAI,CACA,KAAK,OAAS,IAAI,EAAAI,QAAI,CAClB,SAAU,KAAK,OAAO,SACtB,UAAW,KAAK,OAAO,SAC3B,CAAC,CACL,OAASC,EAAY,CACjB,MAAM,IAAIF,EACN,4EAAqBE,EAAM,OAAO,GAClC,qBACAA,EAAM,IACV,CACJ,CACJ,CAGA,IAAY,YAAqB,CAC7B,MAAO,GAAG,KAAK,OAAO,MAAM,IAAI,KAAK,OAAO,KAAK,EACrD,CAEA,MAAa,WAAWC,EAAkBC,EAA+C,CACrF,KAAK,YAAY,EAEjB,IAAMC,EAAgB,CAClB,UAAW,GACX,QAAS,IACT,SAAU,GAAK,KAAO,KACtB,SAAU,EACV,GAAGD,CACP,EAEME,EAAY,KAAK,IAAI,EAE3B,GAAI,CACA,IAAMC,EAAW,KAAK,aAAaJ,CAAQ,EAG3C,GAAI,CAACE,EAAc,UACf,GAAI,CACA,YAAM,KAAK,OAAO,WAAW,CACzB,OAAQ,KAAK,WACb,OAAQ,KAAK,OAAO,OACpB,IAAKA,EAAc,UACvB,CAAC,EACK,IAAIL,EACN,mCAAUK,EAAc,UAAU,GAClC,cACA,qBACJ,CACJ,OAASH,EAAY,CACjB,GAAIA,aAAiBF,EAAgB,MAAME,EAE3C,GAAIA,EAAM,aAAe,IACrB,MAAM,IAAIF,EACN,iEAAeE,EAAM,OAAO,GAC5B,oBACAA,EAAM,IACV,CAER,CAIJ,IAAMM,EAAS,MAAM,KAAK,OAAO,UAAU,CACvC,OAAQ,KAAK,WACb,OAAQ,KAAK,OAAO,OACpB,IAAKH,EAAc,WACnB,QAAM,oBAAiBF,CAAQ,EAC/B,cAAeI,EAAS,KACxB,WAAaE,GAA6B,CACtCJ,EAAc,aAAa,KAAK,MAAMI,EAAO,QAAU,GAAG,CAAC,CAC/D,CACJ,CAAC,EAEKC,EAAW,KAAK,IAAI,EAAIJ,EAC9B,MAAO,CACH,IAAK,KAAK,WAAWD,EAAc,UAAU,EAC7C,KAAMA,EAAc,WACpB,KAAME,EAAS,KACf,SAAAG,EACA,IAAKF,GAAQ,MAAM,QAAQ,KAAM,EAAE,CACvC,CACJ,OAASN,EAAY,CACjB,MAAIA,aAAiBF,EAAsBE,EACrC,IAAIF,EACN,+CAAYE,EAAM,OAAO,GACzB,gBACAA,EAAM,IACV,CACJ,CACJ,CAEO,WAAWS,EAA4B,CAC1C,OAAI,KAAK,OAAO,aACL,GAAG,KAAK,OAAO,OAAS,QAAU,MAAM,MAAM,KAAK,OAAO,YAAY,IAAIA,CAAU,GAGxF,GADU,KAAK,OAAO,OAAS,QAAU,MAC9B,MAAM,KAAK,UAAU,QAAQ,KAAK,OAAO,MAAM,iBAAiBA,CAAU,EAChG,CAEA,MAAa,WAAWA,EAAsC,CAC1D,KAAK,YAAY,EAEjB,GAAI,CACA,aAAM,KAAK,OAAO,aAAa,CAC3B,OAAQ,KAAK,WACb,OAAQ,KAAK,OAAO,OACpB,IAAKA,CACT,CAAC,EACM,EACX,OAAST,EAAY,CAEjB,GAAIA,EAAM,aAAe,IAAK,MAAO,GACrC,MAAM,IAAIF,EACN,2DAAcE,EAAM,OAAO,GAC3B,gBACAA,EAAM,IACV,CACJ,CACJ,CACJ,ECtJA,IAAAU,EAAkB,oBAUlB,IAAMC,EAA4C,CAC9C,GAAI,EAAAC,QAAM,KAAK,QACf,GAAI,EAAAA,QAAM,KAAK,QACf,GAAI,EAAAA,QAAM,KAAK,QACf,IAAK,EAAAA,QAAM,KAAK,SAChB,IAAK,EAAAA,QAAM,KAAK,QACpB,EAQaC,EAAN,cAA+BC,CAAgB,CAGxC,eAAeC,EAAmC,CACxD,IAAMC,EAAcD,EAEdE,EADiB,CAAC,OAAQ,YAAa,YAAa,SAAU,QAAQ,EACvC,OACjCC,GAAS,CAACF,EAAYE,CAA6B,CACvD,EACA,GAAID,EAAc,OAAS,EACvB,MAAM,IAAIE,EACN,6CAAUF,EAAc,KAAK,IAAI,CAAC,GAClC,iBACA,yBACJ,EAEJ,GAAI,CAACN,EAASK,EAAY,IAAI,EAC1B,MAAM,IAAIG,EACN,mCAAeH,EAAY,IAAI,yCAAW,OAAO,KAAKL,CAAQ,EAAE,KAAK,IAAI,CAAC,GAC1E,iBACA,cACJ,EAEJ,MAAO,CAAE,OAAQ,GAAM,GAAGK,CAAY,CAC1C,CAEU,YAAmB,CACzB,GAAI,CACA,IAAMI,EAAM,IAAI,EAAAR,QAAM,KAAK,OAAO,IAAI,KAAK,OAAO,UAAW,KAAK,OAAO,SAAS,EAC5ES,EAAY,IAAI,EAAAT,QAAM,KAAK,OACjCS,EAAU,KAAOV,EAAS,KAAK,OAAO,IAAI,EAC1C,KAAK,OAAS,CACV,IAAAS,EACA,aAAc,IAAI,EAAAR,QAAM,QAAQ,aAAaS,CAAS,EACtD,cAAe,IAAI,EAAAT,QAAM,GAAG,cAAcQ,EAAKC,CAAS,CAC5D,CACJ,OAASC,EAAY,CACjB,MAAM,IAAIH,EACN,uEAAgBG,EAAM,OAAO,GAC7B,qBACAA,EAAM,IACV,CACJ,CACJ,CAEQ,eAAeC,EAAqB,CACxC,IAAMC,EAAS,KAAK,OAIpB,OAHkB,IAAI,EAAAZ,QAAM,GAAG,UAAU,CACrC,MAAO,GAAG,KAAK,OAAO,MAAM,IAAIW,CAAG,EACvC,CAAC,EACgB,YAAYC,EAAO,GAAG,CAC3C,CAEA,MAAa,WAAWC,EAAkBC,EAA+C,CACrF,KAAK,YAAY,EAEjB,IAAMC,EAAgB,CAClB,UAAW,GACX,QAAS,IACT,GAAGD,CACP,EAEME,EAAY,KAAK,IAAI,EAE3B,GAAI,CACA,IAAMC,EAAW,KAAK,aAAaJ,CAAQ,EAG3C,GAAI,CAACE,EAAc,WACA,MAAM,KAAK,gBAAgBA,EAAc,UAAU,EAE9D,MAAM,IAAIR,EACN,mCAAUQ,EAAc,UAAU,GAClC,cACA,mBACJ,EAIR,IAAMG,EAAc,KAAK,eAAeH,EAAc,UAAU,EAC1DI,EAAW,IAAI,EAAAnB,QAAM,QAAQ,SAC7BY,EAAS,KAAK,OAEdQ,EAAS,MAAM,IAAI,QAA0B,CAACC,EAASC,IAAW,CACpEV,EAAO,aAAa,QAChBM,EACAH,EAAc,WACdF,EACAM,EACA,CAACI,EAAwBC,EAAeC,IAAkB,CACtD,GAAIF,EAAK,CAAED,EAAOC,CAAG,EAAG,MAAQ,CAC5BE,EAAS,aAAe,IACxBJ,EAAQG,CAAQ,EAEhBF,EAAO,IAAI,MAAM,qDAAaG,EAAS,UAAU,KAAK,KAAK,UAAUD,CAAQ,CAAC,EAAE,CAAC,CAEzF,CACJ,CACJ,CAAC,EAEKE,EAAW,KAAK,IAAI,EAAIV,EAC9B,MAAO,CACH,IAAK,KAAK,WAAWD,EAAc,UAAU,EAC7C,KAAMA,EAAc,WACpB,KAAME,EAAS,KACf,SAAAS,EACA,IAAKN,GAAQ,IACjB,CACJ,OAASV,EAAY,CACjB,MAAIA,aAAiBH,EAAsBG,EACrC,IAAIH,EACN,+CAAYG,EAAM,OAAO,GACzB,gBACAA,EAAM,IACV,CACJ,CACJ,CAKA,MAAc,gBAAgBC,EAA+B,CACzD,IAAMC,EAAS,KAAK,OACpB,GAAI,CACA,aAAMA,EAAO,cAAc,KAAK,KAAK,OAAO,OAAQD,CAAG,EAChD,EACX,OAASD,EAAY,CACjB,GAAIA,GAAO,aAAe,IAAK,MAAO,GACtC,MAAMA,CACV,CACJ,CAEO,WAAWiB,EAA4B,CAC1C,IAAMC,EAAS,KAAK,OAAO,cAAgB,KAAK,OAAO,OAEvD,MAAO,GADU,KAAK,OAAO,OAAS,QAAU,MAC9B,MAAMA,CAAM,IAAID,CAAU,EAChD,CAEA,MAAa,WAAWA,EAAsC,CAC1D,KAAK,YAAY,EAEjB,GAAI,CAEA,aADe,KAAK,OACP,cAAc,OAAO,KAAK,OAAO,OAAQA,CAAU,EACzD,EACX,OAASjB,EAAY,CAEjB,GAAIA,GAAO,aAAe,IAAK,MAAO,GACtC,MAAM,IAAIH,EACN,2DAAcG,EAAM,OAAO,GAC3B,gBACAA,EAAM,IACV,CACJ,CACJ,CACJ,EL1JO,SAASmB,EAAkBC,EAAoC,CAClE,OAAQA,EAAO,OAAQ,CACnB,IAAK,SACD,OAAO,IAAIC,EAAkBD,CAAM,EACvC,IAAK,UACD,OAAO,IAAIE,EAAmBF,CAAM,EACxC,IAAK,QACD,OAAO,IAAIG,EAAiBH,CAAM,EACtC,QACI,MAAM,IAAI,MAAM,8CAAiBA,EAAe,MAAM,EAAE,CAChE,CACJ","names":["index_exports","__export","AliyunOSSUploader","BaseOSSUploader","OSSUploadError","QiniuOSSUploader","TencentOSSUploader","createOSSUploader","__toCommonJS","import_ali_oss","import_fs","import_fs","OSSUploadError","message","code","requestId","vendorCode","BaseOSSUploader","config","OSSUploadError","filePath","fileStat","AliyunOSSUploader","BaseOSSUploader","config","missingFields","field","OSSUploadError","OSS","error","filePath","options","uploadOptions","startTime","fileStat","fileStream","result","p","costTime","objectName","import_cos_nodejs_sdk_v5","import_fs","TencentOSSUploader","BaseOSSUploader","config","tencentConfig","missingFields","field","OSSUploadError","COS","error","filePath","options","uploadOptions","startTime","fileStat","result","params","costTime","objectName","import_qiniu","ZONE_MAP","qiniu","QiniuOSSUploader","BaseOSSUploader","config","qiniuConfig","missingFields","field","OSSUploadError","mac","cosConfig","error","key","client","filePath","options","uploadOptions","startTime","fileStat","uploadToken","putExtra","result","resolve","reject","err","respBody","respInfo","costTime","objectName","domain","createOSSUploader","config","AliyunOSSUploader","TencentOSSUploader","QiniuOSSUploader"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import F from"ali-oss";import{createReadStream as U}from"fs";import{statSync as E}from"fs";var n=class extends Error{constructor(e,t,i,r){super(e),this.name="OSSUploadError",this.code=t,this.requestId=i,this.vendorCode=r}};var u=class{constructor(e){this.config=this.validateConfig(e),this.initClient()}checkClient(){if(!this.client)throw new n("\u5BA2\u6237\u7AEF\u672A\u521D\u59CB\u5316\u6216\u5DF2\u9500\u6BC1","CLIENT_NOT_INIT")}validateFile(e){let t=E(e);if(!t.isFile())throw new n("\u6307\u5B9A\u8DEF\u5F84\u4E0D\u662F\u6587\u4EF6","NOT_A_FILE");return t}destroy(){this.client=null}};var p=class extends u{validateConfig(e){let i=["region","accessKeyId","accessKeySecret","bucket"].filter(r=>!e[r]);if(i.length>0)throw new n(`\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5\uFF1A${i.join(", ")}`,"INVALID_CONFIG","MISSING_REQUIRED_FIELDS");return{secure:!0,...e}}initClient(){try{this.client=new F({region:this.config.region,accessKeyId:this.config.accessKeyId,accessKeySecret:this.config.accessKeySecret,bucket:this.config.bucket,endpoint:this.config.endpoint})}catch(e){throw new n(`\u521D\u59CB\u5316 OSS \u5BA2\u6237\u7AEF\u5931\u8D25: ${e.message}`,"CLIENT_INIT_FAILED",e.code,e.requestId)}}async uploadFile(e,t){this.checkClient();let i={overwrite:!1,timeout:6e4,partSize:10*1024*1024,parallel:5,...t},r=Date.now();try{let o=this.validateFile(e);if(!i.overwrite)try{throw await this.client.head(i.targetPath),new n(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${i.targetPath}`,"FILE_EXISTS","ALIYUN_FILE_EXISTS")}catch(l){if(l instanceof n)throw l;if(l.code!=="NoSuchKey")throw new n(`\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u5B58\u5728\u5931\u8D25: ${l.message}`,"CHECK_FILE_FAILED",l.code,l.requestId)}let g=U(e),d=await this.client.putStream(i.targetPath,g,{timeout:i.timeout,partSize:i.partSize,parallel:i.parallel,progress:l=>{i.onProgress?.(Math.floor(l*100))}}),s=Date.now()-r;return{url:this.getFileUrl(i.targetPath),path:i.targetPath,size:o.size,costTime:s,md5:d.res.headers["content-md5"]}}catch(o){throw o instanceof n?o:new n(`\u963F\u91CC\u4E91\u4E0A\u4F20\u5931\u8D25: ${o.message}`,"UPLOAD_FAILED",o.code,o.requestId)}}getFileUrl(e){return this.config.customDomain?`${this.config.secure?"https":"http"}://${this.config.customDomain}/${e}`:(this.checkClient(),this.client.signatureUrl(e,{expires:0}))}async deleteFile(e){this.checkClient();try{return await this.client.delete(e),!0}catch(t){if(t.code==="NoSuchKey")return!0;throw new n(`\u963F\u91CC\u4E91\u5220\u9664\u6587\u4EF6\u5931\u8D25: ${t.message}`,"DELETE_FAILED",t.code,t.requestId)}}};import b from"cos-nodejs-sdk-v5";import{createReadStream as _}from"fs";var f=class extends u{validateConfig(e){let t=e,r=["region","secretId","secretKey","bucket","appId"].filter(o=>!t[o]);if(r.length>0)throw new n(`\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5\uFF1A${r.join(", ")}`,"INVALID_CONFIG","MISSING_REQUIRED_FIELDS");return{secure:!0,...t}}initClient(){try{this.client=new b({SecretId:this.config.secretId,SecretKey:this.config.secretKey})}catch(e){throw new n(`\u521D\u59CB\u5316\u817E\u8BAF\u4E91 COS \u5BA2\u6237\u7AEF\u5931\u8D25: ${e.message}`,"CLIENT_INIT_FAILED",e.code)}}get fullBucket(){return`${this.config.bucket}-${this.config.appId}`}async uploadFile(e,t){this.checkClient();let i={overwrite:!1,timeout:6e4,partSize:10*1024*1024,parallel:5,...t},r=Date.now();try{let o=this.validateFile(e);if(!i.overwrite)try{throw await this.client.headObject({Bucket:this.fullBucket,Region:this.config.region,Key:i.targetPath}),new n(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${i.targetPath}`,"FILE_EXISTS","TENCENT_FILE_EXISTS")}catch(s){if(s instanceof n)throw s;if(s.statusCode!==404)throw new n(`\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u5B58\u5728\u5931\u8D25: ${s.message}`,"CHECK_FILE_FAILED",s.code)}let g=await this.client.putObject({Bucket:this.fullBucket,Region:this.config.region,Key:i.targetPath,Body:_(e),ContentLength:o.size,onProgress:s=>{i.onProgress?.(Math.floor(s.percent*100))}}),d=Date.now()-r;return{url:this.getFileUrl(i.targetPath),path:i.targetPath,size:o.size,costTime:d,md5:g?.ETag?.replace(/"/g,"")}}catch(o){throw o instanceof n?o:new n(`\u817E\u8BAF\u4E91\u4E0A\u4F20\u5931\u8D25: ${o.message}`,"UPLOAD_FAILED",o.code)}}getFileUrl(e){return this.config.customDomain?`${this.config.secure?"https":"http"}://${this.config.customDomain}/${e}`:`${this.config.secure?"https":"http"}://${this.fullBucket}.cos.${this.config.region}.myqcloud.com/${e}`}async deleteFile(e){this.checkClient();try{return await this.client.deleteObject({Bucket:this.fullBucket,Region:this.config.region,Key:e}),!0}catch(t){if(t.statusCode===404)return!0;throw new n(`\u817E\u8BAF\u4E91\u5220\u9664\u6587\u4EF6\u5931\u8D25: ${t.message}`,"DELETE_FAILED",t.code)}}};import a from"qiniu";var h={z0:a.zone.Zone_z0,z1:a.zone.Zone_z1,z2:a.zone.Zone_z2,na0:a.zone.Zone_na0,as0:a.zone.Zone_as0},S=class extends u{validateConfig(e){let t=e,r=["zone","accessKey","secretKey","bucket","domain"].filter(o=>!t[o]);if(r.length>0)throw new n(`\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5\uFF1A${r.join(", ")}`,"INVALID_CONFIG","MISSING_REQUIRED_FIELDS");if(!h[t.zone])throw new n(`\u65E0\u6548\u7684 zone \u503C: ${t.zone}\uFF0C\u652F\u6301\u7684\u503C\u4E3A: ${Object.keys(h).join(", ")}`,"INVALID_CONFIG","INVALID_ZONE");return{secure:!0,...t}}initClient(){try{let e=new a.auth.digest.Mac(this.config.accessKey,this.config.secretKey),t=new a.conf.Config;t.zone=h[this.config.zone],this.client={mac:e,formUploader:new a.form_up.FormUploader(t),bucketManager:new a.rs.BucketManager(e,t)}}catch(e){throw new n(`\u521D\u59CB\u5316\u4E03\u725B\u4E91\u5BA2\u6237\u7AEF\u5931\u8D25: ${e.message}`,"CLIENT_INIT_FAILED",e.code)}}getUploadToken(e){let t=this.client;return new a.rs.PutPolicy({scope:`${this.config.bucket}:${e}`}).uploadToken(t.mac)}async uploadFile(e,t){this.checkClient();let i={overwrite:!1,timeout:6e4,...t},r=Date.now();try{let o=this.validateFile(e);if(!i.overwrite&&await this.checkFileExists(i.targetPath))throw new n(`\u6587\u4EF6\u5DF2\u5B58\u5728: ${i.targetPath}`,"FILE_EXISTS","QINIU_FILE_EXISTS");let g=this.getUploadToken(i.targetPath),d=new a.form_up.PutExtra,s=this.client,l=await new Promise((m,C)=>{s.formUploader.putFile(g,i.targetPath,e,d,(O,y,w)=>{if(O){C(O);return}w.statusCode===200?m(y):C(new Error(`\u4E0A\u4F20\u5931\u8D25\uFF0C\u72B6\u6001\u7801: ${w.statusCode}, ${JSON.stringify(y)}`))})}),I=Date.now()-r;return{url:this.getFileUrl(i.targetPath),path:i.targetPath,size:o.size,costTime:I,md5:l?.hash}}catch(o){throw o instanceof n?o:new n(`\u4E03\u725B\u4E91\u4E0A\u4F20\u5931\u8D25: ${o.message}`,"UPLOAD_FAILED",o.code)}}async checkFileExists(e){let t=this.client;try{return await t.bucketManager.stat(this.config.bucket,e),!0}catch(i){if(i?.statusCode===612)return!1;throw i}}getFileUrl(e){let t=this.config.customDomain||this.config.domain;return`${this.config.secure?"https":"http"}://${t}/${e}`}async deleteFile(e){this.checkClient();try{return await this.client.bucketManager.delete(this.config.bucket,e),!0}catch(t){if(t?.statusCode===612)return!0;throw new n(`\u4E03\u725B\u4E91\u5220\u9664\u6587\u4EF6\u5931\u8D25: ${t.message}`,"DELETE_FAILED",t.code)}}};function ue(c){switch(c.vendor){case"aliyun":return new p(c);case"tencent":return new f(c);case"qiniu":return new S(c);default:throw new Error(`\u4E0D\u652F\u6301\u7684 OSS \u5382\u5546: ${c.vendor}`)}}export{p as AliyunOSSUploader,u as BaseOSSUploader,n as OSSUploadError,S as QiniuOSSUploader,f as TencentOSSUploader,ue as createOSSUploader};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/AliyunOSSUploader.ts","../src/core/BaseOSSUploader.ts","../src/types/index.ts","../src/adapters/TencentOSSUploader.ts","../src/adapters/QiniuOSSUploader.ts","../src/index.ts"],"sourcesContent":["import OSS from \"ali-oss\";\r\nimport { createReadStream } from \"fs\";\r\nimport { BaseOSSUploader } from \"../core/BaseOSSUploader.ts\";\r\nimport {\r\n AliyunOSSConfig,\r\n OSSUploadError,\r\n UploadOptions,\r\n UploadResult\r\n} from \"../types/index.ts\";\r\n\r\nexport class AliyunOSSUploader extends BaseOSSUploader {\r\n declare protected config: AliyunOSSConfig;\r\n\r\n protected validateConfig(config: AliyunOSSConfig): AliyunOSSConfig {\r\n const requiredFields = ['region', 'accessKeyId', 'accessKeySecret', 'bucket'];\r\n const missingFields = requiredFields.filter(\r\n field => !config[field as keyof AliyunOSSConfig]\r\n );\r\n if (missingFields.length > 0) {\r\n throw new OSSUploadError(\r\n `缺少必要字段:${missingFields.join(', ')}`,\r\n 'INVALID_CONFIG',\r\n 'MISSING_REQUIRED_FIELDS'\r\n );\r\n }\r\n return { secure: true, ...config };\r\n }\r\n\r\n protected initClient(): void {\r\n try {\r\n this.client = new OSS({\r\n region: this.config.region,\r\n accessKeyId: this.config.accessKeyId,\r\n accessKeySecret: this.config.accessKeySecret,\r\n bucket: this.config.bucket,\r\n endpoint: this.config.endpoint\r\n });\r\n } catch (error: any) {\r\n throw new OSSUploadError(\r\n `初始化 OSS 客户端失败: ${error.message}`,\r\n 'CLIENT_INIT_FAILED',\r\n error.code,\r\n error.requestId\r\n );\r\n }\r\n }\r\n\r\n public async uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult> {\r\n this.checkClient();\r\n\r\n const uploadOptions = {\r\n overwrite: false,\r\n timeout: 60000,\r\n partSize: 10 * 1024 * 1024,\r\n parallel: 5,\r\n ...options\r\n };\r\n\r\n const startTime = Date.now();\r\n\r\n try {\r\n const fileStat = this.validateFile(filePath);\r\n\r\n // 检查文件是否存在\r\n if (!uploadOptions.overwrite) {\r\n try {\r\n await this.client.head(uploadOptions.targetPath);\r\n throw new OSSUploadError(\r\n `文件已存在: ${uploadOptions.targetPath}`,\r\n 'FILE_EXISTS',\r\n 'ALIYUN_FILE_EXISTS'\r\n );\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n if (error.code !== 'NoSuchKey') {\r\n throw new OSSUploadError(\r\n `检查文件是否存在失败: ${error.message}`,\r\n 'CHECK_FILE_FAILED',\r\n error.code,\r\n error.requestId\r\n );\r\n }\r\n }\r\n }\r\n\r\n // 执行上传\r\n const fileStream = createReadStream(filePath);\r\n const result = await this.client.putStream(\r\n uploadOptions.targetPath,\r\n fileStream,\r\n {\r\n timeout: uploadOptions.timeout,\r\n partSize: uploadOptions.partSize,\r\n parallel: uploadOptions.parallel,\r\n progress: (p: number) => {\r\n uploadOptions.onProgress?.(Math.floor(p * 100));\r\n }\r\n }\r\n );\r\n\r\n const costTime = Date.now() - startTime;\r\n return {\r\n url: this.getFileUrl(uploadOptions.targetPath),\r\n path: uploadOptions.targetPath,\r\n size: fileStat.size,\r\n costTime,\r\n md5: result.res.headers['content-md5']\r\n };\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n throw new OSSUploadError(\r\n `阿里云上传失败: ${error.message}`,\r\n 'UPLOAD_FAILED',\r\n error.code,\r\n error.requestId\r\n );\r\n }\r\n }\r\n\r\n public getFileUrl(objectName: string): string {\r\n // 优先使用自定义域名,无需客户端\r\n if (this.config.customDomain) {\r\n return `${this.config.secure ? 'https' : 'http'}://${this.config.customDomain}/${objectName}`;\r\n }\r\n // signatureUrl 需要客户端\r\n this.checkClient();\r\n return this.client.signatureUrl(objectName, { expires: 0 });\r\n }\r\n\r\n public async deleteFile(objectName: string): Promise<boolean> {\r\n this.checkClient();\r\n\r\n try {\r\n await this.client.delete(objectName);\r\n return true;\r\n } catch (error: any) {\r\n if (error.code === 'NoSuchKey') return true;\r\n throw new OSSUploadError(\r\n `阿里云删除文件失败: ${error.message}`,\r\n 'DELETE_FAILED',\r\n error.code,\r\n error.requestId\r\n );\r\n }\r\n }\r\n}\r\n","import { statSync, Stats } from 'fs';\r\nimport { OSSConfig, OSSUploadError, UploadOptions, UploadResult } from \"../types/index.ts\";\r\n\r\n/**\r\n * OSS 上传器抽象基类\r\n * 定义所有厂商上传器必须实现的核心方法\r\n */\r\nexport abstract class BaseOSSUploader {\r\n protected config: OSSConfig;\r\n protected client: any; // 具体厂商的 SDK 客户端实例\r\n\r\n constructor(config: OSSConfig) {\r\n this.config = this.validateConfig(config);\r\n this.initClient();\r\n }\r\n\r\n /** 验证配置合法性(子类实现) */\r\n protected abstract validateConfig(config: OSSConfig): OSSConfig;\r\n /** 初始化厂商客户端(子类实现) */\r\n protected abstract initClient(): void;\r\n /** 上传本地文件 */\r\n public abstract uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult>;\r\n /** 删除文件 */\r\n public abstract deleteFile(objectName: string): Promise<boolean>;\r\n /** 获取文件访问 URL */\r\n public abstract getFileUrl(objectName: string): string;\r\n\r\n /**\r\n * 检查客户端是否可用,destroy() 后调用会抛出 CLIENT_NOT_INIT\r\n */\r\n protected checkClient(): void {\r\n if (!this.client) {\r\n throw new OSSUploadError('客户端未初始化或已销毁', 'CLIENT_NOT_INIT');\r\n }\r\n }\r\n\r\n /**\r\n * 校验本地文件路径合法性,返回文件 Stats\r\n * 路径不存在或不是文件时抛出 NOT_A_FILE\r\n */\r\n protected validateFile(filePath: string): Stats {\r\n const fileStat = statSync(filePath);\r\n if (!fileStat.isFile()) {\r\n throw new OSSUploadError('指定路径不是文件', 'NOT_A_FILE');\r\n }\r\n return fileStat;\r\n }\r\n\r\n /**\r\n * 销毁客户端,释放资源\r\n */\r\n public destroy(): void {\r\n this.client = null;\r\n }\r\n}\r\n","/**\r\n * @description 支持的 OSS 的厂商类型\r\n *\r\n * */\r\nexport type OSSVendor = 'aliyun' | 'tencent' | 'qiniu'\r\n\r\n/**\r\n * @description 通用 OSS 配置项 (不同的厂商的配置会在这个配置上扩展)\r\n * @param vendor: OSSVendor OSS 厂商类型\r\n * @param secure?: boolean 是否使用 HTTPS\r\n * @param customDomain?: string 自定义域名\r\n * */\r\nexport interface OSSConfigBase {\r\n vendor: OSSVendor;\r\n secure?: boolean;\r\n customDomain?: string;\r\n}\r\n\r\n\r\n/**\r\n * @description 阿里云 OSS 配置\r\n * @param region: string OSS地域\r\n * @param accessKeyId: string OSS AccessKeyId\r\n * @param accessKeySecret: string OSS AccessKeySecret\r\n * @param bucket: string OSS 存储空间名称\r\n * @param endpoint?: string OSS 域名\r\n * */\r\nexport interface AliyunOSSConfig extends OSSConfigBase{\r\n vendor: 'aliyun';\r\n region: string;\r\n accessKeyId: string;\r\n accessKeySecret: string;\r\n bucket: string;\r\n endpoint?: string;\r\n}\r\n/**\r\n * @description 腾讯云 COS 配置\r\n * @param region: string COS 地域\r\n * @param secretId: string COS AccessKeyId\r\n * @param secretKey: string COS AccessKeySecret\r\n * @param bucket: string COS 存储空间名称\r\n * @param appId: string 腾讯云 AppId\r\n * */\r\nexport interface TencentOSSConfig extends OSSConfigBase{\r\n vendor: 'tencent';\r\n region: string;\r\n secretId: string;\r\n secretKey: string;\r\n bucket: string;\r\n appId: string;\r\n}\r\n/**\r\n * @description 七牛云 OSS 配置\r\n * @param zone: string 七牛云 OSS 地域( z0:华东, z1:华北, z2:华南, na0:北美, as0:东南亚)\r\n * @param accessKey: string 七牛云 OSS AccessKeyId\r\n * @param secretKey: string 七牛云 OSS SecretKey\r\n * @param bucket: string 七牛云 OSS 存储空间名称\r\n * @param domain: string 七牛云 OSS 域名\r\n * */\r\nexport interface QiniuOSSConfig extends OSSConfigBase{\r\n vendor: 'qiniu';\r\n zone: string;\r\n accessKey: string;\r\n secretKey: string;\r\n bucket: string;\r\n domain: string;\r\n}\r\n/**\r\n * 联合类型:所有 OSS 配置\r\n */\r\nexport type OSSConfig = AliyunOSSConfig | TencentOSSConfig | QiniuOSSConfig;\r\n\r\n/**\r\n * @description 上传的选项\r\n * @param targetPath: string 上传到 OSS 的目标路径\r\n * @param overwrite?: boolean 是否覆盖同名文件\r\n * @param timeout?: number 上传超时时间\r\n * @param partSize?: number 分片大小\r\n * @param parallel?: number 分片上传的并发数\r\n * @param onProgress?: (progress: number) => void 上传进度回调\r\n**/\r\nexport interface UploadOptions {\r\n targetPath: string;\r\n overwrite?: boolean;\r\n timeout?: number;\r\n partSize?: number;\r\n parallel?: number;\r\n onProgress?: (progress: number) => void;\r\n}\r\n\r\n/**\r\n * @description 上传结果\r\n * @param url: string 上传后的 URL\r\n * @param path: string 上传后的路径\r\n * @param size: number 上传的文件大小\r\n * @param costTime: number 上传耗时\r\n * @param md5?: string 文件的 md5 值\r\n */\r\nexport interface UploadResult {\r\n url: string;\r\n path: string;\r\n size: number;\r\n costTime: number;\r\n md5?: string;\r\n}\r\n/**\r\n * @description OSS 错误类型\r\n * */\r\nexport class OSSUploadError extends Error {\r\n /** 错误码 */\r\n code: string;\r\n /** 请求 ID(部分厂商提供) */\r\n requestId?: string;\r\n /** 厂商错误码 */\r\n vendorCode?: string;\r\n constructor(message: string, code: string, requestId?: string,vendorCode?: string) {\r\n super(message);\r\n this.name = 'OSSUploadError';\r\n this.code = code;\r\n this.requestId = requestId;\r\n this.vendorCode = vendorCode;\r\n }\r\n}","import COS from 'cos-nodejs-sdk-v5';\r\nimport { createReadStream } from 'fs';\r\nimport { BaseOSSUploader } from '../core/BaseOSSUploader.ts';\r\nimport {\r\n TencentOSSConfig,\r\n OSSConfig,\r\n OSSUploadError,\r\n UploadOptions,\r\n UploadResult\r\n} from '../types/index.ts';\r\n\r\nexport class TencentOSSUploader extends BaseOSSUploader {\r\n declare protected config: TencentOSSConfig;\r\n\r\n protected validateConfig(config: OSSConfig): TencentOSSConfig {\r\n const tencentConfig = config as TencentOSSConfig;\r\n const requiredFields = ['region', 'secretId', 'secretKey', 'bucket', 'appId'];\r\n const missingFields = requiredFields.filter(\r\n field => !tencentConfig[field as keyof TencentOSSConfig]\r\n );\r\n if (missingFields.length > 0) {\r\n throw new OSSUploadError(\r\n `缺少必要字段:${missingFields.join(', ')}`,\r\n 'INVALID_CONFIG',\r\n 'MISSING_REQUIRED_FIELDS'\r\n );\r\n }\r\n return { secure: true, ...tencentConfig };\r\n }\r\n\r\n protected initClient(): void {\r\n try {\r\n this.client = new COS({\r\n SecretId: this.config.secretId,\r\n SecretKey: this.config.secretKey,\r\n });\r\n } catch (error: any) {\r\n throw new OSSUploadError(\r\n `初始化腾讯云 COS 客户端失败: ${error.message}`,\r\n 'CLIENT_INIT_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n\r\n /** 完整存储桶名称,格式:BucketName-AppId */\r\n private get fullBucket(): string {\r\n return `${this.config.bucket}-${this.config.appId}`;\r\n }\r\n\r\n public async uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult> {\r\n this.checkClient();\r\n\r\n const uploadOptions = {\r\n overwrite: false,\r\n timeout: 60000,\r\n partSize: 10 * 1024 * 1024,\r\n parallel: 5,\r\n ...options\r\n };\r\n\r\n const startTime = Date.now();\r\n\r\n try {\r\n const fileStat = this.validateFile(filePath);\r\n\r\n // 检查文件是否存在\r\n if (!uploadOptions.overwrite) {\r\n try {\r\n await this.client.headObject({\r\n Bucket: this.fullBucket,\r\n Region: this.config.region,\r\n Key: uploadOptions.targetPath,\r\n });\r\n throw new OSSUploadError(\r\n `文件已存在: ${uploadOptions.targetPath}`,\r\n 'FILE_EXISTS',\r\n 'TENCENT_FILE_EXISTS'\r\n );\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n // statusCode 404 表示文件不存在,可以继续上传\r\n if (error.statusCode !== 404) {\r\n throw new OSSUploadError(\r\n `检查文件是否存在失败: ${error.message}`,\r\n 'CHECK_FILE_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n }\r\n\r\n // 执行上传\r\n const result = await this.client.putObject({\r\n Bucket: this.fullBucket,\r\n Region: this.config.region,\r\n Key: uploadOptions.targetPath,\r\n Body: createReadStream(filePath),\r\n ContentLength: fileStat.size,\r\n onProgress: (params: COS.ProgressInfo) => {\r\n uploadOptions.onProgress?.(Math.floor(params.percent * 100));\r\n },\r\n });\r\n\r\n const costTime = Date.now() - startTime;\r\n return {\r\n url: this.getFileUrl(uploadOptions.targetPath),\r\n path: uploadOptions.targetPath,\r\n size: fileStat.size,\r\n costTime,\r\n md5: result?.ETag?.replace(/\"/g, '')\r\n };\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n throw new OSSUploadError(\r\n `腾讯云上传失败: ${error.message}`,\r\n 'UPLOAD_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n\r\n public getFileUrl(objectName: string): string {\r\n if (this.config.customDomain) {\r\n return `${this.config.secure ? 'https' : 'http'}://${this.config.customDomain}/${objectName}`;\r\n }\r\n const protocol = this.config.secure ? 'https' : 'http';\r\n return `${protocol}://${this.fullBucket}.cos.${this.config.region}.myqcloud.com/${objectName}`;\r\n }\r\n\r\n public async deleteFile(objectName: string): Promise<boolean> {\r\n this.checkClient();\r\n\r\n try {\r\n await this.client.deleteObject({\r\n Bucket: this.fullBucket,\r\n Region: this.config.region,\r\n Key: objectName,\r\n });\r\n return true;\r\n } catch (error: any) {\r\n // 404 视为已删除\r\n if (error.statusCode === 404) return true;\r\n throw new OSSUploadError(\r\n `腾讯云删除文件失败: ${error.message}`,\r\n 'DELETE_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n}\r\n","import qiniu from 'qiniu';\r\nimport { BaseOSSUploader } from '../core/BaseOSSUploader.ts';\r\nimport {\r\n QiniuOSSConfig,\r\n OSSConfig,\r\n OSSUploadError,\r\n UploadOptions,\r\n UploadResult\r\n} from '../types/index.ts';\r\n\r\nconst ZONE_MAP: Record<string, qiniu.conf.Zone> = {\r\n z0: qiniu.zone.Zone_z0,\r\n z1: qiniu.zone.Zone_z1,\r\n z2: qiniu.zone.Zone_z2,\r\n na0: qiniu.zone.Zone_na0,\r\n as0: qiniu.zone.Zone_as0,\r\n};\r\n\r\ninterface QiniuClient {\r\n mac: qiniu.auth.digest.Mac;\r\n formUploader: qiniu.form_up.FormUploader;\r\n bucketManager: qiniu.rs.BucketManager;\r\n}\r\n\r\nexport class QiniuOSSUploader extends BaseOSSUploader {\r\n declare protected config: QiniuOSSConfig;\r\n\r\n protected validateConfig(config: OSSConfig): QiniuOSSConfig {\r\n const qiniuConfig = config as QiniuOSSConfig;\r\n const requiredFields = ['zone', 'accessKey', 'secretKey', 'bucket', 'domain'];\r\n const missingFields = requiredFields.filter(\r\n field => !qiniuConfig[field as keyof QiniuOSSConfig]\r\n );\r\n if (missingFields.length > 0) {\r\n throw new OSSUploadError(\r\n `缺少必要字段:${missingFields.join(', ')}`,\r\n 'INVALID_CONFIG',\r\n 'MISSING_REQUIRED_FIELDS'\r\n );\r\n }\r\n if (!ZONE_MAP[qiniuConfig.zone]) {\r\n throw new OSSUploadError(\r\n `无效的 zone 值: ${qiniuConfig.zone},支持的值为: ${Object.keys(ZONE_MAP).join(', ')}`,\r\n 'INVALID_CONFIG',\r\n 'INVALID_ZONE'\r\n );\r\n }\r\n return { secure: true, ...qiniuConfig };\r\n }\r\n\r\n protected initClient(): void {\r\n try {\r\n const mac = new qiniu.auth.digest.Mac(this.config.accessKey, this.config.secretKey);\r\n const cosConfig = new qiniu.conf.Config();\r\n cosConfig.zone = ZONE_MAP[this.config.zone];\r\n this.client = {\r\n mac,\r\n formUploader: new qiniu.form_up.FormUploader(cosConfig),\r\n bucketManager: new qiniu.rs.BucketManager(mac, cosConfig),\r\n } as QiniuClient;\r\n } catch (error: any) {\r\n throw new OSSUploadError(\r\n `初始化七牛云客户端失败: ${error.message}`,\r\n 'CLIENT_INIT_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n\r\n private getUploadToken(key: string): string {\r\n const client = this.client as QiniuClient;\r\n const putPolicy = new qiniu.rs.PutPolicy({\r\n scope: `${this.config.bucket}:${key}`\r\n });\r\n return putPolicy.uploadToken(client.mac);\r\n }\r\n\r\n public async uploadFile(filePath: string, options: UploadOptions): Promise<UploadResult> {\r\n this.checkClient();\r\n\r\n const uploadOptions = {\r\n overwrite: false,\r\n timeout: 60000,\r\n ...options\r\n };\r\n\r\n const startTime = Date.now();\r\n\r\n try {\r\n const fileStat = this.validateFile(filePath);\r\n\r\n // 检查文件是否存在\r\n if (!uploadOptions.overwrite) {\r\n const exists = await this.checkFileExists(uploadOptions.targetPath);\r\n if (exists) {\r\n throw new OSSUploadError(\r\n `文件已存在: ${uploadOptions.targetPath}`,\r\n 'FILE_EXISTS',\r\n 'QINIU_FILE_EXISTS'\r\n );\r\n }\r\n }\r\n\r\n const uploadToken = this.getUploadToken(uploadOptions.targetPath);\r\n const putExtra = new qiniu.form_up.PutExtra();\r\n const client = this.client as QiniuClient;\r\n\r\n const result = await new Promise<{ hash: string }>((resolve, reject) => {\r\n client.formUploader.putFile(\r\n uploadToken,\r\n uploadOptions.targetPath,\r\n filePath,\r\n putExtra,\r\n (err: Error | undefined, respBody: any, respInfo: any) => {\r\n if (err) { reject(err); return; }\r\n if (respInfo.statusCode === 200) {\r\n resolve(respBody);\r\n } else {\r\n reject(new Error(`上传失败,状态码: ${respInfo.statusCode}, ${JSON.stringify(respBody)}`));\r\n }\r\n }\r\n );\r\n });\r\n\r\n const costTime = Date.now() - startTime;\r\n return {\r\n url: this.getFileUrl(uploadOptions.targetPath),\r\n path: uploadOptions.targetPath,\r\n size: fileStat.size,\r\n costTime,\r\n md5: result?.hash\r\n };\r\n } catch (error: any) {\r\n if (error instanceof OSSUploadError) throw error;\r\n throw new OSSUploadError(\r\n `七牛云上传失败: ${error.message}`,\r\n 'UPLOAD_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * 检查文件是否存在:stat 成功则存在,以 612 拒绝则不存在,其余错误上抛\r\n */\r\n private async checkFileExists(key: string): Promise<boolean> {\r\n const client = this.client as QiniuClient;\r\n try {\r\n await client.bucketManager.stat(this.config.bucket, key);\r\n return true;\r\n } catch (error: any) {\r\n if (error?.statusCode === 612) return false;\r\n throw error;\r\n }\r\n }\r\n\r\n public getFileUrl(objectName: string): string {\r\n const domain = this.config.customDomain || this.config.domain;\r\n const protocol = this.config.secure ? 'https' : 'http';\r\n return `${protocol}://${domain}/${objectName}`;\r\n }\r\n\r\n public async deleteFile(objectName: string): Promise<boolean> {\r\n this.checkClient();\r\n\r\n try {\r\n const client = this.client as QiniuClient;\r\n await client.bucketManager.delete(this.config.bucket, objectName);\r\n return true;\r\n } catch (error: any) {\r\n // 612 表示文件不存在,也视为删除成功\r\n if (error?.statusCode === 612) return true;\r\n throw new OSSUploadError(\r\n `七牛云删除文件失败: ${error.message}`,\r\n 'DELETE_FAILED',\r\n error.code\r\n );\r\n }\r\n }\r\n}\r\n","export { AliyunOSSUploader } from './adapters/AliyunOSSUploader.ts';\r\nexport { TencentOSSUploader } from './adapters/TencentOSSUploader.ts';\r\nexport { QiniuOSSUploader } from './adapters/QiniuOSSUploader.ts';\r\nexport { BaseOSSUploader } from './core/BaseOSSUploader.ts';\r\nexport type {\r\n OSSVendor,\r\n OSSConfigBase,\r\n AliyunOSSConfig,\r\n TencentOSSConfig,\r\n QiniuOSSConfig,\r\n OSSConfig,\r\n UploadOptions,\r\n UploadResult\r\n} from './types/index.ts';\r\nexport { OSSUploadError } from './types/index.ts';\r\n\r\nimport { AliyunOSSUploader } from './adapters/AliyunOSSUploader.ts';\r\nimport { TencentOSSUploader } from './adapters/TencentOSSUploader.ts';\r\nimport { QiniuOSSUploader } from './adapters/QiniuOSSUploader.ts';\r\nimport { BaseOSSUploader } from './core/BaseOSSUploader.ts';\r\nimport type { OSSConfig } from './types/index.ts';\r\n\r\n/**\r\n * 工厂函数:根据配置中的 vendor 字段自动创建对应厂商的 OSS 上传器\r\n */\r\nexport function createOSSUploader(config: OSSConfig): BaseOSSUploader {\r\n switch (config.vendor) {\r\n case 'aliyun':\r\n return new AliyunOSSUploader(config);\r\n case 'tencent':\r\n return new TencentOSSUploader(config);\r\n case 'qiniu':\r\n return new QiniuOSSUploader(config);\r\n default:\r\n throw new Error(`不支持的 OSS 厂商: ${(config as any).vendor}`);\r\n }\r\n}\r\n"],"mappings":"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","names":["OSS","createReadStream","statSync","OSSUploadError","message","code","requestId","vendorCode","BaseOSSUploader","config","OSSUploadError","filePath","fileStat","statSync","AliyunOSSUploader","BaseOSSUploader","config","missingFields","field","OSSUploadError","OSS","error","filePath","options","uploadOptions","startTime","fileStat","fileStream","createReadStream","result","p","costTime","objectName","COS","createReadStream","TencentOSSUploader","BaseOSSUploader","config","tencentConfig","missingFields","field","OSSUploadError","COS","error","filePath","options","uploadOptions","startTime","fileStat","result","createReadStream","params","costTime","objectName","qiniu","ZONE_MAP","qiniu","QiniuOSSUploader","BaseOSSUploader","config","qiniuConfig","missingFields","field","OSSUploadError","mac","cosConfig","error","key","client","filePath","options","uploadOptions","startTime","fileStat","uploadToken","putExtra","result","resolve","reject","err","respBody","respInfo","costTime","objectName","domain","createOSSUploader","config","AliyunOSSUploader","TencentOSSUploader","QiniuOSSUploader"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "xin_oss-upload",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "上传到oss的工具",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"clean": "rimraf dist",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:watch": "vitest",
|
|
20
|
+
"test:coverage": "vitest run --coverage",
|
|
21
|
+
"prepublishOnly": "pnpm run clean && pnpm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"oss",
|
|
25
|
+
"upload"
|
|
26
|
+
],
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "ISC",
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"packageManager": "pnpm@10.20.0",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/ali-oss": "^6.23.3",
|
|
35
|
+
"@types/node": "^25.3.2",
|
|
36
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
37
|
+
"nodemon": "^3.1.14",
|
|
38
|
+
"rimraf": "^6.1.3",
|
|
39
|
+
"ts-node": "^10.9.2",
|
|
40
|
+
"tsup": "^8.5.1",
|
|
41
|
+
"typescript": "^5.9.3",
|
|
42
|
+
"vitest": "^4.0.18"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"ali-oss": "^6.23.0",
|
|
46
|
+
"axios": "^1.13.5",
|
|
47
|
+
"cos-nodejs-sdk-v5": "^2.15.4",
|
|
48
|
+
"qiniu": "^7.15.1"
|
|
49
|
+
}
|
|
50
|
+
}
|