@file-ud.js/core 0.1.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 +382 -0
- package/README_DOWNLOADER.md +334 -0
- package/README_UPLOADER.md +369 -0
- package/dist/downloader/index.d.mts +1 -0
- package/dist/downloader/index.d.ts +1 -0
- package/dist/downloader/index.js +8 -0
- package/dist/downloader/index.mjs +1 -0
- package/dist/index.d.mts +1856 -0
- package/dist/index.d.ts +1856 -0
- package/dist/index.js +8413 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +8389 -0
- package/dist/index.mjs.map +1 -0
- package/dist/uploader/index.d.mts +1 -0
- package/dist/uploader/index.d.ts +1 -0
- package/dist/uploader/index.js +8 -0
- package/dist/uploader/index.mjs +1 -0
- package/dist/utils/index.d.mts +2048 -0
- package/dist/utils/index.d.ts +2048 -0
- package/dist/utils/index.js +1609 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +1567 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +94 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# Uploader 使用指南
|
|
2
|
+
|
|
3
|
+
## 简介
|
|
4
|
+
|
|
5
|
+
`Uploader` 是 `file-UD` 库中的文件上传模块,支持文件选择、表单校验、分片并发上传、断点续传、秒传(instant upload)、速率统计、进度跟踪等功能。
|
|
6
|
+
|
|
7
|
+
## 快速开始
|
|
8
|
+
|
|
9
|
+
### 1. 基础用法
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { FileUD } from "@file-ud.js/core";
|
|
13
|
+
|
|
14
|
+
// 创建上传器实例
|
|
15
|
+
const uploader = FileUD.createUploader("myUploader", {
|
|
16
|
+
action: "/api/upload",
|
|
17
|
+
multiple: true, // 支持多选
|
|
18
|
+
autoUpload: true, // 选择后自动开始上传
|
|
19
|
+
accept: ["image/*", ".pdf"],
|
|
20
|
+
maxSize: 10 * 1024 * 1024, // 最大 10MB
|
|
21
|
+
limit: 5, // 最多 5 个文件
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// 设置成功回调
|
|
25
|
+
uploader.onSuccess = (response, file) => {
|
|
26
|
+
console.log(`上传成功: ${file.fileName}`, response);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// 设置更新回调(所有文件列表变化时触发)
|
|
30
|
+
uploader.onUpdate = (files) => {
|
|
31
|
+
files.forEach((file) => {
|
|
32
|
+
console.log(`${file.fileName}: ${file.percent}%`);
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// 打开文件选择器
|
|
37
|
+
uploader.open();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. 分片上传(断点续传 + 秒传)
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const uploader = FileUD.createUploader("chunkUploader", {
|
|
44
|
+
action: "/api/upload-chunk",
|
|
45
|
+
chunkOptions: {
|
|
46
|
+
chunkSize: 2 * 1024 * 1024, // 2MB 分片
|
|
47
|
+
maxConcurrent: 3, // 最多 3 个并发
|
|
48
|
+
retries: 3, // 失败重试 3 次
|
|
49
|
+
timeout: 10000, // 单分片超时
|
|
50
|
+
enableFileCache: true, // 启用 IndexedDB 缓存(断点续传用)
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 分片初始化回调(查询服务端已存在的分片)
|
|
55
|
+
uploader.onInitChunk = async (uploadFile, totalChunks, fileHash) => {
|
|
56
|
+
const { data } = await checkFile({ fileHash });
|
|
57
|
+
return {
|
|
58
|
+
fileHash: data.fileHash,
|
|
59
|
+
chunks: data.chunks || [], // 已上传的分片索引
|
|
60
|
+
isInstantUpload: data.isInstant, // 秒传标记
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// 分片合并回调
|
|
65
|
+
uploader.onMergeChunk = async (chunkManager) => {
|
|
66
|
+
const { data } = await mergeChunks({
|
|
67
|
+
fileHash: chunkManager.fileHash,
|
|
68
|
+
fileName: chunkManager.uploadFile.fileName,
|
|
69
|
+
totalChunks: chunkManager.totalChunks,
|
|
70
|
+
});
|
|
71
|
+
return data; // 返回的数据传给 onSuccess
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 3. 手动上传模式
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
const uploader = FileUD.createUploader("manualUploader", {
|
|
79
|
+
action: "/api/upload",
|
|
80
|
+
autoUpload: false, // 禁用自动上传
|
|
81
|
+
multiple: true,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
uploader.open(); // 用户选择文件后不会自动开始
|
|
85
|
+
|
|
86
|
+
// 手动提交所有文件
|
|
87
|
+
button.onclick = async () => {
|
|
88
|
+
await uploader.submit();
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// 也可以在文件选择时拦截
|
|
92
|
+
uploader.onSelect = (file) => {
|
|
93
|
+
// 返回 false 拒绝该文件
|
|
94
|
+
return file.size < 100 * 1024 * 1024;
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 4. 自定义上传函数
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const uploader = FileUD.createUploader("customUploader", {
|
|
102
|
+
action: async (formData, uploadFile) => {
|
|
103
|
+
const response = await fetch("/api/upload", {
|
|
104
|
+
method: "POST",
|
|
105
|
+
body: formData,
|
|
106
|
+
headers: {
|
|
107
|
+
Authorization: "Bearer token",
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
return await response.json();
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### 5. 文件操作
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
// 从 uploader.files 获取文件实例
|
|
119
|
+
uploader.files.forEach((file) => {
|
|
120
|
+
file.pause(); // 暂停(仅分片模式)
|
|
121
|
+
file.resume(); // 恢复(仅分片模式)
|
|
122
|
+
file.cancel(); // 取消
|
|
123
|
+
file.retry(); // 重试
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 6. 批量操作
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
uploader.pauseAll(); // 暂停所有
|
|
131
|
+
uploader.resumeAll(); // 恢复所有
|
|
132
|
+
uploader.cancelAll(); // 取消所有
|
|
133
|
+
uploader.retryAll(); // 重试所有失败/取消的任务
|
|
134
|
+
uploader.clearFiles(); // 清空列表
|
|
135
|
+
uploader.submit(); // 提交所有 pending 任务
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### 7. 文件列表回显
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// 从服务端恢复文件列表(显示已上传的文件)
|
|
142
|
+
uploader.setFiles([
|
|
143
|
+
{
|
|
144
|
+
fileId: "file-001",
|
|
145
|
+
fileName: "document.pdf",
|
|
146
|
+
url: "https://cdn.example.com/document.pdf",
|
|
147
|
+
percent: 100,
|
|
148
|
+
status: "success",
|
|
149
|
+
formatSize: "5.23 MB",
|
|
150
|
+
},
|
|
151
|
+
]);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## API 参考
|
|
157
|
+
|
|
158
|
+
### UploaderConfig 配置
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
interface UploaderConfig {
|
|
162
|
+
/** 上传地址:字符串 URL 或函数 */
|
|
163
|
+
action: string | ((formData: FormData, uploadFile: UploadFile) => any);
|
|
164
|
+
/** 是否支持多选,默认 false */
|
|
165
|
+
multiple?: boolean;
|
|
166
|
+
/** 接受的文件类型 */
|
|
167
|
+
accept?: AcceptFileType[] | string[];
|
|
168
|
+
/** 是否自动上传,默认 true */
|
|
169
|
+
autoUpload?: boolean;
|
|
170
|
+
/** 是否显示文件输入框,默认 false */
|
|
171
|
+
show?: boolean;
|
|
172
|
+
/** 挂载的元素 ID */
|
|
173
|
+
elementId?: string;
|
|
174
|
+
/** FormData 中文件的字段名,默认 "file" */
|
|
175
|
+
file?: string | ((fileConfig: FileConfig) => void);
|
|
176
|
+
/** 分片上传配置 */
|
|
177
|
+
chunkOptions?: ChunkOptions | null;
|
|
178
|
+
/** 文件数量限制 */
|
|
179
|
+
limit?: number;
|
|
180
|
+
/** 单文件大小限制(字节) */
|
|
181
|
+
maxSize?: number;
|
|
182
|
+
/** 自定义请求头 */
|
|
183
|
+
headers?: Record<string, any>;
|
|
184
|
+
/** 最大同时传输文件数 */
|
|
185
|
+
maxFileConcurrent?: number;
|
|
186
|
+
/** 自定义 axios 实例 */
|
|
187
|
+
axiosInstance?: AxiosInstance;
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### ChunkOptions 分片配置
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
interface ChunkOptions {
|
|
195
|
+
/** 分片大小(字节) */
|
|
196
|
+
chunkSize?: number;
|
|
197
|
+
/** 分片最大并发数 */
|
|
198
|
+
maxConcurrent?: number;
|
|
199
|
+
/** 失败重试次数 */
|
|
200
|
+
retries?: number | null;
|
|
201
|
+
/** 重试延迟(毫秒) */
|
|
202
|
+
retryDelay?: number;
|
|
203
|
+
/** 单分片超时(毫秒) */
|
|
204
|
+
timeout?: number;
|
|
205
|
+
/** 是否启用 IndexedDB 缓存(断点续传用) */
|
|
206
|
+
enableFileCache?: boolean;
|
|
207
|
+
/** 缓存保留天数,默认 7 天 */
|
|
208
|
+
cacheRetentionDays?: number;
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Uploader 方法
|
|
213
|
+
|
|
214
|
+
| 方法 | 说明 | 返回值 |
|
|
215
|
+
|------|------|--------|
|
|
216
|
+
| `open(fn?)` | 打开文件选择器 | `void` |
|
|
217
|
+
| `use(plugin)` | 注册插件 | `this` |
|
|
218
|
+
| `unuse(name)` | 移除插件 | `this` |
|
|
219
|
+
| `getPlugin(name?)` | 获取插件 | `IUDPlugin \| IUDPlugin[]` |
|
|
220
|
+
| `updateConfig(config)` | 动态更新配置 | `void` |
|
|
221
|
+
| `setFiles(files)` | 回显文件列表 | `void` |
|
|
222
|
+
| `clearFiles()` | 清空文件列表 | `void` |
|
|
223
|
+
| `pauseAll()` | 暂停所有进行中的上传 | `void` |
|
|
224
|
+
| `resumeAll()` | 恢复所有暂停的上传 | `void` |
|
|
225
|
+
| `cancelAll()` | 取消所有上传 | `void` |
|
|
226
|
+
| `retryAll()` | 重试所有失败/取消的任务 | `void` |
|
|
227
|
+
| `submit()` | 提交所有 pending 任务 | `Promise<void>` |
|
|
228
|
+
|
|
229
|
+
### Uploader 静态方法
|
|
230
|
+
|
|
231
|
+
| 方法 | 说明 |
|
|
232
|
+
|------|------|
|
|
233
|
+
| `Uploader.setDefaultPlugins(plugins)` | 设置全局默认插件 |
|
|
234
|
+
|
|
235
|
+
### Uploader 回调设置器
|
|
236
|
+
|
|
237
|
+
| 设置器 | 回调签名 | 说明 |
|
|
238
|
+
|--------|----------|------|
|
|
239
|
+
| `onSuccess = fn` | `(response, file) => void` | 单文件上传成功 |
|
|
240
|
+
| `onUpdate = fn` | `(files: UploadFile[]) => void` | 文件列表更新(进度/状态变化) |
|
|
241
|
+
| `onInitChunk = fn` | `(file, totalChunks, fileHash) => Promise` | 分片初始化(查已上传分片) |
|
|
242
|
+
| `onMergeChunk = fn` | `(chunkManager) => Promise` | 分片合并 |
|
|
243
|
+
| `onbeforeTransfer = fn` | `(file) => boolean \| Promise` | 上传前拦截 |
|
|
244
|
+
| `onSelect = fn` | `(file: File) => boolean \| Promise` | 文件选择时拦截 |
|
|
245
|
+
|
|
246
|
+
### UploadFile 属性
|
|
247
|
+
|
|
248
|
+
| 属性 | 类型 | 说明 |
|
|
249
|
+
|------|------|------|
|
|
250
|
+
| `fileId` | `string` | 文件唯一标识 |
|
|
251
|
+
| `fileName` | `string` | 文件名 |
|
|
252
|
+
| `File` | `File` | 原始 File 对象 |
|
|
253
|
+
| `url` | `string` | 文件预览 URL(Object URL) |
|
|
254
|
+
| `size` | `number` | 文件大小(字节) |
|
|
255
|
+
| `percent` | `number` | 上传进度 (0-100) |
|
|
256
|
+
| `status` | `string` | 状态:pending / UDLoading / paused / success / fail / error / cancelled |
|
|
257
|
+
| `speed` | `speedInfo` | 速率信息(瞬时/平均速度、预计剩余时间) |
|
|
258
|
+
| `formatSize` | `string` | 格式化文件大小 |
|
|
259
|
+
| `transferFormatSize` | `string` | 已上传大小格式化字符串 |
|
|
260
|
+
| `hashPercent` | `number` | MD5 计算进度 (0-100) |
|
|
261
|
+
| `hashLoading` | `boolean` | 是否正在计算 MD5 |
|
|
262
|
+
|
|
263
|
+
### UploadFile 方法
|
|
264
|
+
|
|
265
|
+
| 方法 | 说明 |
|
|
266
|
+
|------|------|
|
|
267
|
+
| `start(chunkManager)` | 开始上传 |
|
|
268
|
+
| `pause()` | 暂停上传(仅分片模式) |
|
|
269
|
+
| `resume()` | 恢复上传(仅分片模式) |
|
|
270
|
+
| `cancel()` | 取消上传 |
|
|
271
|
+
| `retry()` | 重试上传 |
|
|
272
|
+
|
|
273
|
+
### 事件
|
|
274
|
+
|
|
275
|
+
通过 `uploader.on(eventName, callback)` 监听:
|
|
276
|
+
|
|
277
|
+
| 事件名 | 回调参数 | 说明 |
|
|
278
|
+
|--------|----------|------|
|
|
279
|
+
| `change` | `(file: UploadFile)` | 用户选择文件 |
|
|
280
|
+
| `progress` | `(percent: number)` | 全局进度 |
|
|
281
|
+
| `pause` | `(file: UploadFile)` | 文件暂停 |
|
|
282
|
+
| `resume` | `(file: UploadFile)` | 文件恢复 |
|
|
283
|
+
| `cancel` | `(file: UploadFile)` | 文件取消 |
|
|
284
|
+
| `retry` | `(file: UploadFile)` | 文件重试 |
|
|
285
|
+
| `remove` | `(file: UploadFile)` | 文件移除 |
|
|
286
|
+
| `files-start` | `(files: UploadFile[])` | 批量开始 |
|
|
287
|
+
| `files-complete` | `(files: TransferFile[])` | 批量完成 |
|
|
288
|
+
| `chunk-success` | `(data: ChunkSuccessData)` | 分片上传成功 |
|
|
289
|
+
| `chunk-error` | `(data: ChunkErrorData)` | 分片上传失败 |
|
|
290
|
+
| `chunk-upload-start` | `(data: ChunkStartData)` | 分片上传开始 |
|
|
291
|
+
| `instant-upload` | `(data)` | 秒传成功(文件已存在) |
|
|
292
|
+
| `merging` | `(data)` | 分片合并中 |
|
|
293
|
+
| `merge-success` | `(data)` | 分片合并完成 |
|
|
294
|
+
| `merge-error` | `(data)` | 分片合并失败 |
|
|
295
|
+
| `error` | `(error: FileUDErrorJSON)` | 上传错误 |
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## 插件系统
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
const myPlugin = {
|
|
303
|
+
name: "my-upload-plugin",
|
|
304
|
+
version: "1.0.0",
|
|
305
|
+
priority: 50, // 数字越小越优先执行
|
|
306
|
+
|
|
307
|
+
install: (transfer, options) => {
|
|
308
|
+
console.log("插件初始化");
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
onFileSelect: async (file, context) => {
|
|
312
|
+
console.log(`选择了文件: ${file.fileName}`);
|
|
313
|
+
return file; // 可修改 file 后返回,返回 false 拒绝
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
beforeTransfer: async (file, context) => {
|
|
317
|
+
return true; // 返回 false 阻止上传
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
onProgress: (percent, file, context) => {
|
|
321
|
+
console.log(`${file.fileName}: ${percent}%`);
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
onSuccess: (response, file, context) => {
|
|
325
|
+
console.log(`✅ ${file.fileName} 上传成功`);
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
onError: (error, file, context) => {
|
|
329
|
+
console.error(`❌ ${file.fileName} 上传失败:`, error);
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
destroy: () => {
|
|
333
|
+
console.log("插件销毁");
|
|
334
|
+
},
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
uploader.use(myPlugin);
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## 上传流程说明
|
|
343
|
+
|
|
344
|
+
### 普通上传流程
|
|
345
|
+
```
|
|
346
|
+
open() → 用户选择文件 → onFileSelect 钩子 → onSelect 回调
|
|
347
|
+
→ 校验(size/type/limit) → handleFile(生成预览URL)
|
|
348
|
+
→ beforeTransfer 钩子 → FormData 构建
|
|
349
|
+
→ XMLHttpRequest 上传 → 进度回调 → 成功 → onSuccess
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### 分片上传流程
|
|
353
|
+
```
|
|
354
|
+
open() → 用户选择文件 → 校验 → 计算 MD5(增量哈希)
|
|
355
|
+
→ onInitChunk 回调(查服务端已存在分片)
|
|
356
|
+
→ IndexedDB 恢复(如启用 enableFileCache)
|
|
357
|
+
→ 秒传检查(全部已完成 → 跳过上传)
|
|
358
|
+
→ 并发上传缺失分片
|
|
359
|
+
→ 全部分片上传完成
|
|
360
|
+
→ onMergeChunk 回调(服务端合并分片)
|
|
361
|
+
→ onSuccess
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 文件状态流转
|
|
365
|
+
```
|
|
366
|
+
pending → UDLoading → merging → success
|
|
367
|
+
→ paused → UDLoading(恢复)
|
|
368
|
+
→ cancelled / fail / error → retry → UDLoading
|
|
369
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Downloader as default, Downloader, downloaderDefaultConfig as defaultConfig } from "../index.mjs";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Downloader as default, Downloader, downloaderDefaultConfig as defaultConfig } from "../index";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Downloader as default, Downloader, downloaderDefaultConfig as defaultConfig } from "../index.mjs";
|