uniapp-request-sdk 1.6.0 → 1.7.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 +729 -3
- package/dist/index.d.ts +156 -26
- package/dist/index.esm.js +222 -22
- package/dist/index.umd.js +222 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,15 +5,62 @@
|
|
|
5
5
|
[](https://www.npmjs.com/package/uniapp-request-sdk)
|
|
6
6
|
[](https://github.com/yourusername/uniapp-request-sdk)
|
|
7
7
|
|
|
8
|
+
## 目录
|
|
9
|
+
|
|
10
|
+
- [适用场景](#适用场景)
|
|
11
|
+
- [核心能力](#核心能力)
|
|
12
|
+
- [安装](#安装)
|
|
13
|
+
- [5 分钟接入](#5-分钟接入)
|
|
14
|
+
- [快速开始](#快速开始)
|
|
15
|
+
- [工程接入建议](#工程接入建议)
|
|
16
|
+
- [详细配置](#详细配置)
|
|
17
|
+
- [API 文档](#api-文档)
|
|
18
|
+
- [文件上传](#文件上传)
|
|
19
|
+
- [文件下载](#文件下载)
|
|
20
|
+
- [使用示例](#使用示例)
|
|
21
|
+
- [响应格式](#响应格式)
|
|
22
|
+
- [错误处理](#错误处理)
|
|
23
|
+
- [平台差异处理](#平台差异处理)
|
|
24
|
+
- [配置参数详解](#配置参数详解)
|
|
25
|
+
- [请求头预处理器(新增功能)](#请求头预处理器新增功能)
|
|
26
|
+
- [最佳实践](#最佳实践)
|
|
27
|
+
- [企业级实战案例](#企业级实战案例)
|
|
28
|
+
- [常见问题](#常见问题)
|
|
29
|
+
- [版本历史](#版本历史)
|
|
30
|
+
|
|
31
|
+
## 适用场景
|
|
32
|
+
|
|
33
|
+
这个 SDK 适合以下场景:
|
|
34
|
+
|
|
35
|
+
- uni-app 项目中统一封装 `GET`、`POST`、`PUT`、`DELETE`、`uploadFile`、`downloadFile`
|
|
36
|
+
- 需要自动处理 `403` 后重新获取 token 再重试的业务场景
|
|
37
|
+
- 需要为所有请求统一追加公共请求头、日志字段、签名字段
|
|
38
|
+
- 需要在 App、小程序、H5 等多端尽量复用一套网络层逻辑
|
|
39
|
+
- 需要在上传文件时监听进度并驱动页面上的进度条展示
|
|
40
|
+
- 需要在下载文件时监听进度并驱动页面上的下载进度条展示
|
|
41
|
+
|
|
42
|
+
如果你的服务端返回结构不是本文档中的默认格式,也可以基于当前 SDK 继续二次封装。
|
|
43
|
+
|
|
44
|
+
## 核心能力
|
|
45
|
+
|
|
46
|
+
- 统一的请求实例管理,支持运行时动态更新参数
|
|
47
|
+
- 支持全局公共请求头与单次请求头合并
|
|
48
|
+
- 支持同步或异步的请求头预处理器 `headerProcessor`
|
|
49
|
+
- 支持 `401`、`403`、网络异常、超时等通用场景处理
|
|
50
|
+
- 支持文件上传、上传超时控制和上传进度监听
|
|
51
|
+
- 支持文件下载、下载超时控制和下载进度监听
|
|
52
|
+
- 默认兼容相对路径与完整 URL 两种调用方式
|
|
53
|
+
|
|
8
54
|
## 特性
|
|
9
55
|
|
|
10
56
|
- ✅ **自动重试机制** - 请求失败自动重试,可配置重试次数和延迟
|
|
11
57
|
- ✅ **动态 Token 管理** - 支持自动获取和更新 token,兼容 APP 原生交互
|
|
12
58
|
- ✅ **请求头预处理** - 支持同步/异步预处理器,动态生成或修改请求头
|
|
13
59
|
- ✅ **完整的异常处理** - 统一的错误处理、HTTP 状态码处理、权限管理
|
|
14
|
-
- ✅ **文件上传支持** -
|
|
60
|
+
- ✅ **文件上传支持** - 专门优化的文件上传接口,独立超时控制,进度监听
|
|
61
|
+
- ✅ **文件下载支持** - 专门优化的文件下载接口,独立超时控制,进度监听
|
|
15
62
|
- ✅ **平台兼容** - 支持 iOS App、Android App、H5 等多平台
|
|
16
|
-
- ✅ **TypeScript 支持** -
|
|
63
|
+
- ✅ **TypeScript 支持** - 完整的类型定义和 JSDoc 注释,更好的开发体验
|
|
17
64
|
- ✅ **完全向后兼容** - 不破坏现有代码,渐进式增强
|
|
18
65
|
|
|
19
66
|
## 安装
|
|
@@ -30,6 +77,71 @@ npm install uniapp-request-sdk
|
|
|
30
77
|
yarn add uniapp-request-sdk
|
|
31
78
|
```
|
|
32
79
|
|
|
80
|
+
## 5 分钟接入
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import UniRequest from 'uniapp-request-sdk';
|
|
84
|
+
|
|
85
|
+
const request = new UniRequest({
|
|
86
|
+
baseUrl: 'https://api.example.com',
|
|
87
|
+
timeout: 10000,
|
|
88
|
+
uploadTimeout: 30000,
|
|
89
|
+
downloadTimeout: 30000,
|
|
90
|
+
tokenHeader: 'Authorization',
|
|
91
|
+
tokenPrefix: 'Bearer ',
|
|
92
|
+
onErrorHandler: (error) => {
|
|
93
|
+
console.error('请求失败:', error);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
export default request;
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
import request from '@/utils/request';
|
|
102
|
+
|
|
103
|
+
export function getUserProfile() {
|
|
104
|
+
return request.get('/user/profile');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function updateUserProfile(data: Record<string, any>) {
|
|
108
|
+
return request.post('/user/profile/update', data);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function uploadAvatar(filePath: string) {
|
|
112
|
+
return request.uploadFile(
|
|
113
|
+
'/user/avatar/upload',
|
|
114
|
+
filePath,
|
|
115
|
+
undefined,
|
|
116
|
+
'file',
|
|
117
|
+
undefined,
|
|
118
|
+
(progress) => {
|
|
119
|
+
console.log('上传进度:', progress.progress);
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function downloadDocument(documentId: string) {
|
|
125
|
+
return request.downloadFile(
|
|
126
|
+
`/documents/${documentId}/download`,
|
|
127
|
+
undefined,
|
|
128
|
+
undefined,
|
|
129
|
+
(progress) => {
|
|
130
|
+
console.log('下载进度:', progress.progress);
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
完成以上两步后,你的工程已经具备:
|
|
137
|
+
|
|
138
|
+
- 统一请求入口
|
|
139
|
+
- 统一错误处理能力
|
|
140
|
+
- 文件上传能力
|
|
141
|
+
- 上传进度监听能力
|
|
142
|
+
- 文件下载能力
|
|
143
|
+
- 下载进度监听能力
|
|
144
|
+
|
|
33
145
|
## 快速开始
|
|
34
146
|
|
|
35
147
|
### 基础使用
|
|
@@ -58,10 +170,53 @@ await request.put('/users/1', { name: 'Jane' });
|
|
|
58
170
|
// 6. 上传文件
|
|
59
171
|
const uploadResult = await request.uploadFile(
|
|
60
172
|
'/upload',
|
|
61
|
-
'/path/to/file.jpg'
|
|
173
|
+
'/path/to/file.jpg',
|
|
174
|
+
undefined,
|
|
175
|
+
'file',
|
|
176
|
+
undefined,
|
|
177
|
+
(progress) => {
|
|
178
|
+
console.log('当前上传进度:', progress.progress);
|
|
179
|
+
},
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
// 7. 下载文件
|
|
183
|
+
const downloadPath = await request.downloadFile(
|
|
184
|
+
'/download/document.pdf',
|
|
185
|
+
undefined,
|
|
186
|
+
undefined,
|
|
187
|
+
(progress) => {
|
|
188
|
+
console.log('当前下载进度:', progress.progress);
|
|
189
|
+
},
|
|
62
190
|
);
|
|
63
191
|
```
|
|
64
192
|
|
|
193
|
+
## 工程接入建议
|
|
194
|
+
|
|
195
|
+
推荐在业务工程中按下面的方式组织网络层:
|
|
196
|
+
|
|
197
|
+
```text
|
|
198
|
+
src/
|
|
199
|
+
├── api/ # 按业务域拆分接口
|
|
200
|
+
│ ├── user.ts
|
|
201
|
+
│ ├── auth.ts
|
|
202
|
+
│ └── workflow.ts
|
|
203
|
+
├── utils/
|
|
204
|
+
│ └── request.ts # SDK 实例初始化
|
|
205
|
+
└── pages/ # 页面中只调用 api 层
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
推荐职责划分如下:
|
|
209
|
+
|
|
210
|
+
- `utils/request.ts`:只负责创建 `UniRequest` 实例和放置全局配置
|
|
211
|
+
- `api/*.ts`:只负责定义接口函数,不写 UI 逻辑
|
|
212
|
+
- `pages/*` 或 `components/*`:只消费 `api` 层返回的数据
|
|
213
|
+
|
|
214
|
+
推荐这样做的原因:
|
|
215
|
+
|
|
216
|
+
- 请求配置只有一份,便于统一维护
|
|
217
|
+
- token、签名、超时、日志字段不会散落在页面中
|
|
218
|
+
- 后续替换域名、调整 header、增加埋点时改动范围最小
|
|
219
|
+
|
|
65
220
|
## 详细配置
|
|
66
221
|
|
|
67
222
|
### 初始化配置
|
|
@@ -214,8 +369,191 @@ const result = await request.uploadFile<ResponseType>(
|
|
|
214
369
|
'X-Upload-Token': 'upload-token',
|
|
215
370
|
}
|
|
216
371
|
);
|
|
372
|
+
|
|
373
|
+
// 监听上传进度
|
|
374
|
+
const result = await request.uploadFile<ResponseType>(
|
|
375
|
+
'/upload',
|
|
376
|
+
'/path/to/file.jpg',
|
|
377
|
+
{ description: 'My upload' },
|
|
378
|
+
'file',
|
|
379
|
+
undefined,
|
|
380
|
+
(progress) => {
|
|
381
|
+
console.log('上传进度百分比:', progress.progress);
|
|
382
|
+
console.log('已上传字节数:', progress.totalBytesSent);
|
|
383
|
+
console.log('总字节数:', progress.totalBytesExpectedToSend);
|
|
384
|
+
}
|
|
385
|
+
);
|
|
217
386
|
```
|
|
218
387
|
|
|
388
|
+
### 文件下载
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
// 基础文件下载
|
|
392
|
+
const tempFilePath = await request.downloadFile('/download/file.pdf');
|
|
393
|
+
|
|
394
|
+
// 带自定义保存路径的下载(仅小程序有效)
|
|
395
|
+
const filePath = await request.downloadFile(
|
|
396
|
+
'/download/file.pdf',
|
|
397
|
+
'user_documents/file.pdf' // 本地保存路径(仅小程序平台支持)
|
|
398
|
+
);
|
|
399
|
+
|
|
400
|
+
// 自定义请求头的下载
|
|
401
|
+
const filePath = await request.downloadFile(
|
|
402
|
+
'/download/file.pdf',
|
|
403
|
+
undefined, // 保存路径
|
|
404
|
+
{ // 自定义请求头
|
|
405
|
+
'X-Download-Token': 'download-token',
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
// 监听下载进度
|
|
410
|
+
const filePath = await request.downloadFile(
|
|
411
|
+
'/download/large-file.zip',
|
|
412
|
+
undefined,
|
|
413
|
+
undefined,
|
|
414
|
+
(progress) => {
|
|
415
|
+
console.log('下载进度百分比:', progress.progress);
|
|
416
|
+
console.log('已下载字节数:', progress.totalBytesWritten);
|
|
417
|
+
console.log('总字节数:', progress.totalBytesExpectedToWrite);
|
|
418
|
+
}
|
|
419
|
+
);
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## 文件上传
|
|
423
|
+
|
|
424
|
+
### 方法签名
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
uploadFile<T>(
|
|
428
|
+
url: string,
|
|
429
|
+
filePath: string,
|
|
430
|
+
formData?: Record<string, any>,
|
|
431
|
+
name = 'file',
|
|
432
|
+
header?: Record<string, string>,
|
|
433
|
+
onProgressUpdateCallback?: (result: OnProgressUpdateResult) => void,
|
|
434
|
+
): Promise<T>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
### 参数说明
|
|
438
|
+
|
|
439
|
+
| 参数 | 类型 | 是否必填 | 说明 |
|
|
440
|
+
|------|------|----------|------|
|
|
441
|
+
| `url` | `string` | 是 | 上传接口地址,支持相对路径和完整 URL |
|
|
442
|
+
| `filePath` | `string` | 是 | 待上传文件的本地临时路径 |
|
|
443
|
+
| `formData` | `Record<string, any>` | 否 | 附加的表单字段 |
|
|
444
|
+
| `name` | `string` | 否 | 文件字段名,默认是 `file` |
|
|
445
|
+
| `header` | `Record<string, string>` | 否 | 当前上传请求的自定义请求头 |
|
|
446
|
+
| `onProgressUpdateCallback` | `(result) => void` | 否 | 上传进度回调 |
|
|
447
|
+
|
|
448
|
+
### 上传进度回调说明
|
|
449
|
+
|
|
450
|
+
当传入 `onProgressUpdateCallback` 后,SDK 会在内部调用 `uni.uploadFile()` 返回的 `UploadTask.onProgressUpdate()` 进行绑定。
|
|
451
|
+
|
|
452
|
+
回调参数结构如下:
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
type OnProgressUpdateResult = {
|
|
456
|
+
progress?: number;
|
|
457
|
+
totalBytesSent?: number;
|
|
458
|
+
totalBytesExpectedToSend?: number;
|
|
459
|
+
};
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### 使用注意事项
|
|
463
|
+
|
|
464
|
+
- `onProgressUpdateCallback` 是可选参数,不传时上传行为与旧版本保持一致
|
|
465
|
+
- 如果只想传进度回调、不需要自定义 `header`,第 5 个参数需要显式传 `undefined`
|
|
466
|
+
- 上传接口底层仍然返回 Promise,适合继续用 `await` 或 `.then()`
|
|
467
|
+
- 如果上传过程中发生失败并触发重试,进度回调会按”当前这次上传尝试”的进度继续上报
|
|
468
|
+
- 如果服务端返回的是字符串 JSON,SDK 会自动尝试解析后再取其中的 `data`
|
|
469
|
+
|
|
470
|
+
## 文件下载
|
|
471
|
+
|
|
472
|
+
### 新增功能说明
|
|
473
|
+
|
|
474
|
+
从 v1.6.2 版本开始,SDK 新增了 `downloadFile` 方法,提供与 `uploadFile` 对称的文件下载功能。支持:
|
|
475
|
+
|
|
476
|
+
- ✅ 下载文件到本地临时目录或指定路径
|
|
477
|
+
- ✅ 自定义请求头
|
|
478
|
+
- ✅ 实时下载进度监听
|
|
479
|
+
- ✅ 自动重试、Token 管理等企业级特性
|
|
480
|
+
- ✅ 跨平台兼容(App、小程序、H5)
|
|
481
|
+
|
|
482
|
+
### 方法签名
|
|
483
|
+
|
|
484
|
+
```typescript
|
|
485
|
+
downloadFile<T extends string = string>(
|
|
486
|
+
url: string,
|
|
487
|
+
filePath?: string,
|
|
488
|
+
header?: Record<string, string>,
|
|
489
|
+
onProgressUpdateCallback?: (result: DownloadFileProgressUpdateResult) => void,
|
|
490
|
+
): Promise<T>
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### 参数说明
|
|
494
|
+
|
|
495
|
+
| 参数 | 类型 | 是否必填 | 说明 |
|
|
496
|
+
|------|------|----------|------|
|
|
497
|
+
| `url` | `string` | 是 | 下载资源的 URL,支持相对路径和完整 URL |
|
|
498
|
+
| `filePath` | `string` | 否 | 文件保存路径(本地路径),仅小程序平台支持;如不指定则保存到临时目录 |
|
|
499
|
+
| `header` | `Record<string, string>` | 否 | 当前下载请求的自定义请求头 |
|
|
500
|
+
| `onProgressUpdateCallback` | `(result) => void` | 否 | 下载进度回调 |
|
|
501
|
+
|
|
502
|
+
### 返回值说明
|
|
503
|
+
|
|
504
|
+
方法返回 Promise,解析为下载文件的临时路径(`tempFilePath`):
|
|
505
|
+
|
|
506
|
+
```typescript
|
|
507
|
+
// 基础用法
|
|
508
|
+
const filePath = await request.downloadFile('/files/document.pdf');
|
|
509
|
+
// filePath 是文件的临时路径,如 '/tmp/file_20240314.pdf'
|
|
510
|
+
|
|
511
|
+
// 支持泛型约束
|
|
512
|
+
const filePath: string = await request.downloadFile<string>('/files/document.pdf');
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
### 下载进度回调说明
|
|
516
|
+
|
|
517
|
+
当传入 `onProgressUpdateCallback` 后,SDK 会在内部调用 `uni.downloadFile()` 返回的 `DownloadTask.onProgressUpdate()` 进行绑定。
|
|
518
|
+
|
|
519
|
+
回调参数结构如下:
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
type DownloadFileProgressUpdateResult = {
|
|
523
|
+
progress?: number; // 下载进度百分比 (0-100)
|
|
524
|
+
totalBytesWritten?: number; // 已下载字节数
|
|
525
|
+
totalBytesExpectedToWrite?: number; // 总字节数
|
|
526
|
+
};
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### 与上传的区别
|
|
530
|
+
|
|
531
|
+
| 特性 | uploadFile | downloadFile |
|
|
532
|
+
|------|-----------|--------------|
|
|
533
|
+
| 超时默认值 | 5 秒 | 30 秒 |
|
|
534
|
+
| 文件来源 | 本地文件 | 远程 URL |
|
|
535
|
+
| 附加参数 | formData(表单字段) | filePath(保存路径) |
|
|
536
|
+
| 进度类型 | `totalBytesSent` | `totalBytesWritten` |
|
|
537
|
+
| 返回值 | 服务端响应的 data 字段 | 文件临时路径 |
|
|
538
|
+
|
|
539
|
+
### 平台兼容性
|
|
540
|
+
|
|
541
|
+
| 特性 | iOS App | Android App | 小程序 | H5 |
|
|
542
|
+
|------|---------|-----------|--------|-----|
|
|
543
|
+
| 基础下载 | ✅ | ✅ | ✅ | ✅ |
|
|
544
|
+
| 进度监听 | ✅ | ✅ | ✅ | 部分 |
|
|
545
|
+
| 自定义保存路径 | ❌ | ❌ | ✅ | ❌ |
|
|
546
|
+
| 文件 URI | ✅ | ✅ | ✅ | 部分 |
|
|
547
|
+
|
|
548
|
+
### 使用注意事项
|
|
549
|
+
|
|
550
|
+
- 下载的文件保存在临时目录,应用关闭后会被清理,需要持久化时调用 `uni.saveFile()`
|
|
551
|
+
- 小程序平台支持指定 `filePath` 保存到指定目录,其他平台会忽略此参数
|
|
552
|
+
- 下载超时时间默认为 30 秒,可通过 `downloadTimeout` 配置修改
|
|
553
|
+
- 下载过程中如果失败,SDK 会自动重试(最多 `maxRetryCount` 次)
|
|
554
|
+
- 下载大文件时建议增加 `downloadTimeout` 的值
|
|
555
|
+
- 下载过程中用户可以通过返回的 Promise 的 abort 功能中止下载(需要获取 DownloadTask)
|
|
556
|
+
|
|
219
557
|
## 使用示例
|
|
220
558
|
|
|
221
559
|
### 示例 1:基础请求
|
|
@@ -327,6 +665,275 @@ async function selectAndUploadFile() {
|
|
|
327
665
|
}
|
|
328
666
|
```
|
|
329
667
|
|
|
668
|
+
### 示例 4-1:带上传进度的文件上传
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
import UniRequest from 'uniapp-request-sdk';
|
|
672
|
+
|
|
673
|
+
const request = new UniRequest({
|
|
674
|
+
baseUrl: 'https://api.example.com',
|
|
675
|
+
uploadTimeout: 30000,
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
async function selectAndUploadFile() {
|
|
679
|
+
uni.chooseImage({
|
|
680
|
+
count: 1,
|
|
681
|
+
success: async (res) => {
|
|
682
|
+
const filePath = res.tempFilePaths[0];
|
|
683
|
+
let currentProgress = 0;
|
|
684
|
+
|
|
685
|
+
try {
|
|
686
|
+
const result = await request.uploadFile(
|
|
687
|
+
'/upload',
|
|
688
|
+
filePath,
|
|
689
|
+
{
|
|
690
|
+
category: 'avatar',
|
|
691
|
+
},
|
|
692
|
+
'file',
|
|
693
|
+
undefined,
|
|
694
|
+
(progress) => {
|
|
695
|
+
currentProgress = progress.progress || 0;
|
|
696
|
+
console.log('上传中:', currentProgress);
|
|
697
|
+
},
|
|
698
|
+
);
|
|
699
|
+
|
|
700
|
+
console.log('上传完成:', result);
|
|
701
|
+
} catch (error) {
|
|
702
|
+
console.error('上传失败:', error, currentProgress);
|
|
703
|
+
}
|
|
704
|
+
},
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
### 示例 4-2:文件下载(新增)
|
|
710
|
+
|
|
711
|
+
```typescript
|
|
712
|
+
import UniRequest from 'uniapp-request-sdk';
|
|
713
|
+
|
|
714
|
+
const request = new UniRequest({
|
|
715
|
+
baseUrl: 'https://api.example.com',
|
|
716
|
+
downloadTimeout: 30000, // 文件下载超时 30 秒
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
// 简单下载
|
|
720
|
+
async function downloadFile() {
|
|
721
|
+
try {
|
|
722
|
+
const filePath = await request.downloadFile('/files/document.pdf');
|
|
723
|
+
console.log('下载完成,文件路径:', filePath);
|
|
724
|
+
|
|
725
|
+
// 显示文件
|
|
726
|
+
uni.openDocument({
|
|
727
|
+
filePath: filePath,
|
|
728
|
+
showMenu: true,
|
|
729
|
+
});
|
|
730
|
+
} catch (error) {
|
|
731
|
+
console.error('下载失败:', error);
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
// 带进度监听的下载
|
|
736
|
+
async function downloadFileWithProgress() {
|
|
737
|
+
try {
|
|
738
|
+
let downloadProgress = 0;
|
|
739
|
+
|
|
740
|
+
const filePath = await request.downloadFile(
|
|
741
|
+
'/files/large-file.zip',
|
|
742
|
+
undefined,
|
|
743
|
+
undefined,
|
|
744
|
+
(progress) => {
|
|
745
|
+
downloadProgress = progress.progress || 0;
|
|
746
|
+
console.log(`下载进度: ${downloadProgress}%`);
|
|
747
|
+
console.log(`已下载: ${progress.totalBytesWritten} / ${progress.totalBytesExpectedToWrite} 字节`);
|
|
748
|
+
|
|
749
|
+
// 更新 UI 进度条
|
|
750
|
+
// updateProgressBar(downloadProgress);
|
|
751
|
+
}
|
|
752
|
+
);
|
|
753
|
+
|
|
754
|
+
console.log('下载完成:', filePath);
|
|
755
|
+
} catch (error) {
|
|
756
|
+
console.error('下载失败:', error, downloadProgress);
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// 小程序中下载到指定路径
|
|
761
|
+
async function downloadFileToPath() {
|
|
762
|
+
try {
|
|
763
|
+
const filePath = await request.downloadFile(
|
|
764
|
+
'/files/contract.pdf',
|
|
765
|
+
'user_documents/contract.pdf' // 保存到应用沙箱
|
|
766
|
+
);
|
|
767
|
+
|
|
768
|
+
console.log('文件已保存:', filePath);
|
|
769
|
+
} catch (error) {
|
|
770
|
+
console.error('下载失败:', error);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// 带自定义 header 的下载(如需要鉴权)
|
|
775
|
+
async function downloadSecureFile() {
|
|
776
|
+
try {
|
|
777
|
+
const filePath = await request.downloadFile(
|
|
778
|
+
'/files/secure-document.pdf',
|
|
779
|
+
undefined,
|
|
780
|
+
{
|
|
781
|
+
'X-Download-Token': 'secure-token-xxx',
|
|
782
|
+
'X-User-Id': 'user-123',
|
|
783
|
+
},
|
|
784
|
+
(progress) => {
|
|
785
|
+
console.log(`下载进度: ${progress.progress}%`);
|
|
786
|
+
}
|
|
787
|
+
);
|
|
788
|
+
|
|
789
|
+
console.log('下载完成:', filePath);
|
|
790
|
+
} catch (error) {
|
|
791
|
+
console.error('下载失败:', error);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
### 示例 4-3:综合文件管理(上传和下载)
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
import UniRequest from 'uniapp-request-sdk';
|
|
800
|
+
|
|
801
|
+
const request = new UniRequest({
|
|
802
|
+
baseUrl: 'https://api.example.com',
|
|
803
|
+
uploadTimeout: 30000,
|
|
804
|
+
downloadTimeout: 30000,
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
// 文件管理服务
|
|
808
|
+
export const FileService = {
|
|
809
|
+
// 上传文件
|
|
810
|
+
uploadDocument: async (filePath: string, metadata: Record<string, any>) => {
|
|
811
|
+
try {
|
|
812
|
+
const result = await request.uploadFile(
|
|
813
|
+
'/documents/upload',
|
|
814
|
+
filePath,
|
|
815
|
+
metadata,
|
|
816
|
+
'file',
|
|
817
|
+
undefined,
|
|
818
|
+
(progress) => {
|
|
819
|
+
console.log(`上传进度: ${progress.progress}%`);
|
|
820
|
+
}
|
|
821
|
+
);
|
|
822
|
+
return result;
|
|
823
|
+
} catch (error) {
|
|
824
|
+
console.error('上传失败:', error);
|
|
825
|
+
throw error;
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
|
|
829
|
+
// 下载文件
|
|
830
|
+
downloadDocument: async (documentId: string, fileName: string) => {
|
|
831
|
+
try {
|
|
832
|
+
const filePath = await request.downloadFile(
|
|
833
|
+
`/documents/download/${documentId}`,
|
|
834
|
+
`downloads/${fileName}`,
|
|
835
|
+
{ 'X-Document-Id': documentId },
|
|
836
|
+
(progress) => {
|
|
837
|
+
console.log(`下载进度: ${progress.progress}%`);
|
|
838
|
+
}
|
|
839
|
+
);
|
|
840
|
+
return filePath;
|
|
841
|
+
} catch (error) {
|
|
842
|
+
console.error('下载失败:', error);
|
|
843
|
+
throw error;
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
|
|
847
|
+
// 列出文档
|
|
848
|
+
listDocuments: async (payload: Record<string, any>) => {
|
|
849
|
+
return request.post('/documents/list', payload);
|
|
850
|
+
},
|
|
851
|
+
|
|
852
|
+
// 删除文档
|
|
853
|
+
deleteDocument: async (documentId: string) => {
|
|
854
|
+
return request.delete(`/documents/${documentId}`);
|
|
855
|
+
},
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
// 在组件中使用
|
|
859
|
+
export default {
|
|
860
|
+
data() {
|
|
861
|
+
return {
|
|
862
|
+
uploadProgress: 0,
|
|
863
|
+
downloadProgress: 0,
|
|
864
|
+
documents: [],
|
|
865
|
+
};
|
|
866
|
+
},
|
|
867
|
+
|
|
868
|
+
methods: {
|
|
869
|
+
// 选择并上传文件
|
|
870
|
+
async selectAndUpload() {
|
|
871
|
+
try {
|
|
872
|
+
const res = await uni.chooseFile({
|
|
873
|
+
type: 'file',
|
|
874
|
+
count: 1,
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
const result = await FileService.uploadDocument(
|
|
878
|
+
res.tempFilePaths[0],
|
|
879
|
+
{
|
|
880
|
+
description: '重要文档',
|
|
881
|
+
category: 'reports',
|
|
882
|
+
}
|
|
883
|
+
);
|
|
884
|
+
|
|
885
|
+
uni.showToast({
|
|
886
|
+
title: '上传成功',
|
|
887
|
+
icon: 'success',
|
|
888
|
+
});
|
|
889
|
+
|
|
890
|
+
// 刷新列表
|
|
891
|
+
await this.loadDocuments();
|
|
892
|
+
} catch (error) {
|
|
893
|
+
uni.showToast({
|
|
894
|
+
title: '上传失败',
|
|
895
|
+
icon: 'error',
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
|
|
900
|
+
// 下载文件
|
|
901
|
+
async downloadDocument(documentId: string, fileName: string) {
|
|
902
|
+
try {
|
|
903
|
+
const filePath = await FileService.downloadDocument(documentId, fileName);
|
|
904
|
+
|
|
905
|
+
// 打开文件
|
|
906
|
+
uni.openDocument({
|
|
907
|
+
filePath: filePath,
|
|
908
|
+
showMenu: true,
|
|
909
|
+
});
|
|
910
|
+
} catch (error) {
|
|
911
|
+
uni.showToast({
|
|
912
|
+
title: '下载失败',
|
|
913
|
+
icon: 'error',
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
},
|
|
917
|
+
|
|
918
|
+
// 加载文档列表
|
|
919
|
+
async loadDocuments() {
|
|
920
|
+
try {
|
|
921
|
+
this.documents = await FileService.listDocuments({
|
|
922
|
+
page: 1,
|
|
923
|
+
limit: 20,
|
|
924
|
+
});
|
|
925
|
+
} catch (error) {
|
|
926
|
+
console.error('加载失败:', error);
|
|
927
|
+
}
|
|
928
|
+
},
|
|
929
|
+
},
|
|
930
|
+
|
|
931
|
+
mounted() {
|
|
932
|
+
this.loadDocuments();
|
|
933
|
+
},
|
|
934
|
+
};
|
|
935
|
+
```
|
|
936
|
+
|
|
330
937
|
### 示例 5:实时生成签名
|
|
331
938
|
|
|
332
939
|
```typescript
|
|
@@ -973,6 +1580,8 @@ export default {
|
|
|
973
1580
|
|
|
974
1581
|
|
|
975
1582
|
|
|
1583
|
+
## 常见问题
|
|
1584
|
+
|
|
976
1585
|
### Q: 如何处理跨域问题?
|
|
977
1586
|
|
|
978
1587
|
A: 在 `baseUrl` 中包含代理路径,让后端或开发服务器代理请求。
|
|
@@ -999,6 +1608,97 @@ await Promise.all(
|
|
|
999
1608
|
);
|
|
1000
1609
|
```
|
|
1001
1610
|
|
|
1611
|
+
### Q: 如何下载多个文件?
|
|
1612
|
+
|
|
1613
|
+
A: 需要多次调用 `downloadFile`,建议使用串行下载避免超时:
|
|
1614
|
+
|
|
1615
|
+
```typescript
|
|
1616
|
+
// 方式 1:顺序下载(推荐)
|
|
1617
|
+
const filePaths = [];
|
|
1618
|
+
for (const fileUrl of fileUrls) {
|
|
1619
|
+
const path = await request.downloadFile(fileUrl);
|
|
1620
|
+
filePaths.push(path);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// 方式 2:并行下载(需谨慎,可能导致超时)
|
|
1624
|
+
const filePaths = await Promise.all(
|
|
1625
|
+
fileUrls.map(fileUrl => request.downloadFile(fileUrl))
|
|
1626
|
+
);
|
|
1627
|
+
```
|
|
1628
|
+
|
|
1629
|
+
### Q: 下载的文件如何持久化?
|
|
1630
|
+
|
|
1631
|
+
A: 使用 `uni.saveFile()` 将临时文件保存到永久目录:
|
|
1632
|
+
|
|
1633
|
+
```typescript
|
|
1634
|
+
const tempFilePath = await request.downloadFile('/file.pdf');
|
|
1635
|
+
|
|
1636
|
+
// 保存到永久位置
|
|
1637
|
+
uni.saveFile({
|
|
1638
|
+
tempFilePath: tempFilePath,
|
|
1639
|
+
success: (res) => {
|
|
1640
|
+
console.log('文件已保存:', res.savedFilePath);
|
|
1641
|
+
},
|
|
1642
|
+
});
|
|
1643
|
+
```
|
|
1644
|
+
|
|
1645
|
+
### Q: 下载大文件时超时怎么办?
|
|
1646
|
+
|
|
1647
|
+
A: 增加 `downloadTimeout` 的值:
|
|
1648
|
+
|
|
1649
|
+
```typescript
|
|
1650
|
+
const request = new UniRequest({
|
|
1651
|
+
baseUrl: 'https://api.example.com',
|
|
1652
|
+
downloadTimeout: 60000, // 60 秒,适合大文件
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
// 或在运行时修改
|
|
1656
|
+
request.setParams({
|
|
1657
|
+
downloadTimeout: 120000, // 120 秒
|
|
1658
|
+
});
|
|
1659
|
+
```
|
|
1660
|
+
|
|
1661
|
+
### Q: 如何取消正在进行的下载?
|
|
1662
|
+
|
|
1663
|
+
A: 目前库返回的是 Promise,无法直接获取 DownloadTask 来调用 abort。建议通过以下方式实现:
|
|
1664
|
+
|
|
1665
|
+
```typescript
|
|
1666
|
+
// 方式 1:修改库的源码返回 DownloadTask(高级用法)
|
|
1667
|
+
// 方式 2:使用超时机制让请求自动中止
|
|
1668
|
+
// 方式 3:在下载进度回调中检查用户是否点击了取消
|
|
1669
|
+
|
|
1670
|
+
// 推荐:在进度回调中检查状态
|
|
1671
|
+
let shouldCancel = false;
|
|
1672
|
+
|
|
1673
|
+
await request.downloadFile(
|
|
1674
|
+
'/file.zip',
|
|
1675
|
+
undefined,
|
|
1676
|
+
undefined,
|
|
1677
|
+
(progress) => {
|
|
1678
|
+
if (shouldCancel) {
|
|
1679
|
+
// 无法直接中止,但可以停止处理
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
console.log(`下载进度: ${progress.progress}%`);
|
|
1683
|
+
}
|
|
1684
|
+
);
|
|
1685
|
+
|
|
1686
|
+
// 用户点击取消按钮时
|
|
1687
|
+
function cancelDownload() {
|
|
1688
|
+
shouldCancel = true;
|
|
1689
|
+
}
|
|
1690
|
+
```
|
|
1691
|
+
|
|
1692
|
+
### Q: 下载的文件在哪里?
|
|
1693
|
+
|
|
1694
|
+
A: 下载的文件保存在以下位置:
|
|
1695
|
+
|
|
1696
|
+
- **H5**: 浏览器默认下载目录
|
|
1697
|
+
- **小程序**: 临时目录(`wx.env.USER_DATA_PATH` 下),或指定的 `filePath`
|
|
1698
|
+
- **App**: 应用的缓存目录
|
|
1699
|
+
|
|
1700
|
+
建议下载后立即使用或保存,避免应用被清理时文件丢失。
|
|
1701
|
+
|
|
1002
1702
|
### Q: 如何处理 token 过期?
|
|
1003
1703
|
|
|
1004
1704
|
A: 库会自动在收到 403 状态码时获取新 token 并重试。如需自定义逻辑:
|
|
@@ -1033,6 +1733,32 @@ A: 是的,包括重试时都会执行。这是为了确保每次请求都获
|
|
|
1033
1733
|
|
|
1034
1734
|
## 版本历史
|
|
1035
1735
|
|
|
1736
|
+
### v1.6.2(最新)
|
|
1737
|
+
|
|
1738
|
+
- ✨ **新增** 文件下载功能 (`downloadFile` 方法)
|
|
1739
|
+
- 支持下载文件到本地目录或临时目录
|
|
1740
|
+
- 支持实时下载进度监听
|
|
1741
|
+
- 支持自定义请求头
|
|
1742
|
+
- 自动继承 token、重试等企业级特性
|
|
1743
|
+
- 默认超时时间 30 秒(可配置)
|
|
1744
|
+
|
|
1745
|
+
- 📝 **文档改进** 完整的 JSDoc 注释
|
|
1746
|
+
- 所有公开/私有方法添加详细 JSDoc
|
|
1747
|
+
- 所有参数和返回值有清晰说明
|
|
1748
|
+
- 关键方法包含使用示例
|
|
1749
|
+
- 100% 注释覆盖率
|
|
1750
|
+
|
|
1751
|
+
- 📚 **README 更新**
|
|
1752
|
+
- 新增《文件下载》文档章节
|
|
1753
|
+
- 新增文件下载的详细使用示例(示例 4-2、4-3)
|
|
1754
|
+
- 更新目录索引
|
|
1755
|
+
- 完善参数和平台兼容性说明
|
|
1756
|
+
|
|
1757
|
+
- 🔧 **技术改进**
|
|
1758
|
+
- 优化 TypeScript 类型定义
|
|
1759
|
+
- 完善错误处理机制
|
|
1760
|
+
- 增强跨平台兼容性检查
|
|
1761
|
+
|
|
1036
1762
|
### v1.4.11
|
|
1037
1763
|
|
|
1038
1764
|
- ✨ **新增** 请求头预处理器功能 (`headerProcessor`)
|