Skip to content

Commit 0070e6e

Browse files
committed
append
1 parent ac8f70c commit 0070e6e

File tree

8 files changed

+63
-78
lines changed

8 files changed

+63
-78
lines changed

README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
基于七牛 API 开发的前端 JavaScript SDK
88

9-
### 当前版本为 3.x,旧版本文档:[2.x](https://github.com/qiniu/js-sdk/tree/2.x)[1.x](https://github.com/qiniu/js-sdk/tree/1.x)
9+
## 当前版本为 3.x,旧版本文档:[2.x](https://github.com/qiniu/js-sdk/tree/2.x)[1.x](https://github.com/qiniu/js-sdk/tree/1.x)
1010

1111
### 2.x 升级到 3.x 的注意事项请参考 [文档](https://github.com/qiniu/js-sdk/wiki/2.x-%E5%8D%87%E7%BA%A7%E5%88%B0-3.x-%E6%96%87%E6%A1%A3%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9)
1212

@@ -81,17 +81,15 @@ Qiniu-JavaScript-SDK 的示例 [Demo](http://jssdk-v2.demo.qiniu.io) 中的服
8181

8282
* 直接使用静态文件地址:
8383

84-
```
85-
https://cdnjs.cloudflare.com/ajax/libs/qiniu-js/<version>/qiniu.min.js
86-
```
84+
`https://cdnjs.cloudflare.com/ajax/libs/qiniu-js/<version>/qiniu.min.js`
8785

88-
通过sctipt标签引入该文件,会在全局生成名为 `qiniu` 的对象
86+
通过 script 标签引入该文件,会在全局生成名为 `qiniu` 的对象
8987

9088
* 使用 NPM 安装
9189

9290
NPM 的全称是 Node Package Manager,是一个 [NodeJS](https://nodejs.org) 包管理和分发工具,已经成为了非官方的发布 Node 模块(包)的标准。如果需要更详细的关于 NPM 的使用说明,您可以访问 [NPM 官方网站](https://www.npmjs.com),或对应的 [中文网站](http://www.npmjs.com.cn/)
9391

94-
```
92+
```shell
9593
npm install qiniu-js
9694
```
9795

@@ -164,6 +162,7 @@ qiniu.compressImage(file, options).then(data => {
164162
}
165163
}
166164
```
165+
167166
* next: 接收上传进度信息的回调函数,回调函数参数值为 `object`,包含字段信息如下:
168167
* uploadInfo: `object`,只有分片上传时才返回该字段
169168
* uploadInfo.id: 上传任务的唯一标识。
@@ -174,16 +173,15 @@ qiniu.compressImage(file, options).then(data => {
174173
* total.percent: `number`,当前上传进度,范围:0100
175174

176175
* error: 上传错误后触发;自动重试本身并不会触发该错误,而当重试次数到达上限后则可以触发。当不是 xhr 请求错误时,会把当前错误产生原因直接抛出,诸如 JSON 解析异常等;当产生 xhr 请求错误时,参数 err 的类型为 `QiniuError`, 对于请求错误,err 的类型为 `QiniuRequestError`(继承自`QiniuError`),如果是由于非服务端原因发生错误时(例如断网、跨域等等),错误的类型为 `QiniuNetworkError`(继承自`QiniuRequestError`)。
177-
* `QiniuError`
176+
* `QiniuError` (实现了 `Error`)
178177
* name: `QiniuErrorName` 错误的类型。
179178
* message `string` 错误的信息。
180-
* `QiniuRequestError`
179+
* `QiniuRequestError` (继承自 `QiniuError`)
181180
* reqId: `string` xhr 请求错误的 `X-Reqid`
182181
* code: `number` 请求错误状态码,可查阅码值对应 [说明](https://developer.qiniu.com/kodo/api/3928/error-responses)。
183182
* isRequestError: 用于区分是否为 xhr 请求错误;当 xhr 请求出现错误并且后端通过 HTTP 状态码返回了错误信息时,该参数为 `true`;否则为 `undefined`
184-
* `QiniuNetworkError`
185-
* code: `number` 固定为 `0`
186-
* reqId: 由于请求还未真正发出、无法收集到 `reqId`,所以该字段固定为 `''`
183+
* `QiniuNetworkError` (继承自 `QiniuRequestError`)
184+
* reqId:由于请求可能还未真正发出、所以可能无法收集到 `reqId`,该字段可能为 `''`
187185

188186
* complete: 接收上传完成后的后端返回信息,具体返回结构取决于后端sdk的配置,可参考 [上传策略](https://developer.qiniu.com/kodo/manual/1206/put-policy)。
189187

src/api/index.mock.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { QiniuNetworkError, QiniuRequestError } from '../errors'
22
import * as api from '.'
33

44
export const errorMap = {
5-
networkError: new QiniuNetworkError('message'), // 网络错误
5+
networkError: new QiniuNetworkError('mock', 'message'), // 网络错误
66

77
invalidParams: new QiniuRequestError(400, 'mock', 'message'), // 无效的参数
88
expiredToken: new QiniuRequestError(401, 'mock', 'message'), // token 过期

src/errors/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,11 @@ export class QiniuRequestError extends QiniuError {
4747
}
4848
}
4949

50+
/**
51+
* @description 由于跨域、证书错误、断网、host 解析失败、系统拦截等原因导致的错误
52+
*/
5053
export class QiniuNetworkError extends QiniuRequestError {
51-
constructor(message: string) {
52-
super(0, '', message)
54+
constructor(reqId: string, message: string) {
55+
super(0, reqId, message)
5356
}
5457
}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
export { QiniuErrorName, QiniuError, QiniuRequestError, QiniuNetworkError } from './errors'
12
export { imageMogr2, watermark, imageInfo, exif, pipeline } from './image'
2-
export { QiniuErrorName, QiniuError, QiniuRequestError } from './errors'
33
export { deleteUploadedChunks, getUploadUrl } from './api'
44
export { default as upload } from './upload'
55
export { region } from './config'

src/upload/hosts.ts

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,10 @@ export class HostPool {
5555
private cachedHostsMap = new Map<string, Host[]>()
5656

5757
/**
58-
* @param {string[]} hosts
59-
* @description 如果在构造时传入 hosts,则该 host 池始终使用传入的 host 做为可用的数据
58+
* @param {string[]} initHosts
59+
* @description 如果在构造时传入 initHosts,则该 host 池始终使用传入的 initHosts 做为可用的数据
6060
*/
61-
constructor(private hosts?: string[]) {
62-
}
61+
constructor(private initHosts: string[] = []) { }
6362

6463
/**
6564
* @param {string} accessKey
@@ -84,20 +83,22 @@ export class HostPool {
8483
* @description 从接口刷新最新的 host 数据,如果用户在构造时该类时传入了 host、则不会发起请求、而是固定使用传入的数据。
8584
*/
8685
private async refresh(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol']): Promise<void> {
87-
const hosts: string[] = []
88-
if (this.hosts != null && this.hosts.length > 0) {
89-
hosts.push(...this.hosts)
90-
} else {
91-
const response = await getUpHosts(accessKey, bucketName, protocol)
92-
if (response?.data != null) {
93-
hosts.push(
94-
...(response.data.up?.acc?.main || []),
95-
...(response.data.up?.acc?.backup || [])
96-
)
97-
}
86+
const cachedHostList = this.cachedHostsMap.get(`${accessKey}@${bucketName}`) || []
87+
if (cachedHostList.length > 0) return
88+
89+
if (this.initHosts.length > 0) {
90+
this.register(accessKey, bucketName, this.initHosts, protocol)
91+
return
9892
}
9993

100-
this.register(accessKey, bucketName, hosts, protocol)
94+
const response = await getUpHosts(accessKey, bucketName, protocol)
95+
if (response?.data != null) {
96+
const stashHosts: string[] = [
97+
...(response.data.up?.acc?.main || []),
98+
...(response.data.up?.acc?.backup || [])
99+
]
100+
this.register(accessKey, bucketName, stashHosts, protocol)
101+
}
101102
}
102103

103104
/**
@@ -108,13 +109,9 @@ export class HostPool {
108109
* @returns {Promise<Host | null>}
109110
* @description 获取一个可用的上传 Host,排除已冻结的
110111
*/
111-
public async getUp(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol'], isRefresh = false): Promise<Host | null> {
112-
if (isRefresh) await this.refresh(accessKey, bucketName, protocol)
113-
112+
public async getUp(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol']): Promise<Host | null> {
113+
await this.refresh(accessKey, bucketName, protocol)
114114
const cachedHostList = this.cachedHostsMap.get(`${accessKey}@${bucketName}`) || []
115-
if (cachedHostList.length === 0 && !isRefresh) {
116-
return this.getUp(accessKey, bucketName, protocol, true)
117-
}
118115

119116
if (cachedHostList.length === 0) return null
120117
const availableHostList = cachedHostList.filter(host => !host.isFrozen())

src/upload/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default function upload(
5858
}
5959

6060
// 创建 host 池
61-
const hostPool = new HostPool(options.config?.uphost)
61+
const hostPool = new HostPool(options.config.uphost)
6262

6363
return new Observable((observer: IObserver<UploadProgress, QiniuError, UploadCompleteData>) => {
6464
const manager = createUploadManager(options, {

src/upload/resume.ts

Lines changed: 7 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,14 @@ export default class Resume extends Base {
8383
} catch (error) {
8484
// uploadId 无效,上传参数有误(多由于本地存储信息的 uploadId 失效
8585
if (error instanceof QiniuRequestError && (error.code === 612 || error.code === 400)) {
86-
try {
87-
utils.removeLocalFileInfo(localKey)
88-
} catch (removeError) {
89-
this.logger.warn(removeError)
90-
}
86+
utils.removeLocalFileInfo(localKey, this.logger)
9187
}
9288

9389
throw error
9490
}
9591

9692
// 上传成功,清理本地缓存数据
97-
try {
98-
utils.removeLocalFileInfo(localKey)
99-
} catch (error) {
100-
this.logger.warn(error)
101-
}
93+
utils.removeLocalFileInfo(localKey, this.logger)
10294
return mkFileResponse
10395
}
10496

@@ -157,14 +149,10 @@ export default class Resume extends Base {
157149
size: chunk.size
158150
}
159151

160-
try {
161-
utils.setLocalFileInfo(this.getLocalKey(), {
162-
id: this.uploadId,
163-
data: this.uploadedList
164-
})
165-
} catch (error) {
166-
this.logger.info(`set part ${index} cache failed.`, error)
167-
}
152+
utils.setLocalFileInfo(this.getLocalKey(), {
153+
id: this.uploadId,
154+
data: this.uploadedList
155+
}, this.logger)
168156
}
169157

170158
private async mkFileReq() {
@@ -196,12 +184,7 @@ export default class Resume extends Base {
196184
}
197185

198186
private async initBeforeUploadChunks() {
199-
let localInfo: LocalInfo | null = null
200-
try {
201-
localInfo = utils.getLocalFileInfo(this.getLocalKey())
202-
} catch (error) {
203-
this.logger.warn(error)
204-
}
187+
const localInfo = utils.getLocalFileInfo(this.getLocalKey(), this.logger)
205188

206189
// 分片必须和当时使用的 uploadId 配套,所以断点续传需要把本地存储的 uploadId 拿出来
207190
// 假如没有 localInfo 本地信息并重新获取 uploadId

src/utils/helper.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import SparkMD5 from 'spark-md5'
2-
import { urlSafeBase64Decode } from './base64'
3-
import { Progress, LocalInfo } from '../upload'
2+
43
import { QiniuErrorName, QiniuError, QiniuRequestError, QiniuNetworkError } from '../errors'
4+
import { Progress, LocalInfo } from '../upload'
5+
import Logger from '../logger'
6+
7+
import { urlSafeBase64Decode } from './base64'
58

69
export const MB = 1024 ** 2
710

@@ -43,14 +46,14 @@ export function sum(list: number[]) {
4346
return list.reduce((data, loaded) => data + loaded, 0)
4447
}
4548

46-
export function setLocalFileInfo(localKey: string, info: LocalInfo) {
49+
export function setLocalFileInfo(localKey: string, info: LocalInfo, logger: Logger) {
4750
try {
4851
localStorage.setItem(localKey, JSON.stringify(info))
4952
} catch (err) {
50-
throw new QiniuError(
53+
logger.warn(new QiniuError(
5154
QiniuErrorName.WriteCacheFailed,
5255
`setLocalFileInfo failed: ${localKey}`
53-
)
56+
))
5457
}
5558
}
5659

@@ -59,26 +62,26 @@ export function createLocalKey(name: string, key: string | null | undefined, siz
5962
return `qiniu_js_sdk_upload_file_name_${name}${localKey}size_${size}`
6063
}
6164

62-
export function removeLocalFileInfo(localKey: string) {
65+
export function removeLocalFileInfo(localKey: string, logger: Logger) {
6366
try {
6467
localStorage.removeItem(localKey)
6568
} catch (err) {
66-
throw new QiniuError(
69+
logger.warn(new QiniuError(
6770
QiniuErrorName.RemoveCacheFailed,
6871
`removeLocalFileInfo failed. key: ${localKey}`
69-
)
72+
))
7073
}
7174
}
7275

73-
export function getLocalFileInfo(localKey: string): LocalInfo | null {
76+
export function getLocalFileInfo(localKey: string, logger: Logger): LocalInfo | null {
7477
let localInfoString: string | null = null
7578
try {
7679
localInfoString = localStorage.getItem(localKey)
7780
} catch {
78-
throw new QiniuError(
81+
logger.warn(new QiniuError(
7982
QiniuErrorName.ReadCacheFailed,
8083
`getLocalFileInfo failed. key: ${localKey}`
81-
)
84+
))
8285
}
8386

8487
if (localInfoString == null) {
@@ -90,11 +93,11 @@ export function getLocalFileInfo(localKey: string): LocalInfo | null {
9093
localInfo = JSON.parse(localInfoString)
9194
} catch {
9295
// 本地信息已被破坏,直接删除
93-
removeLocalFileInfo(localKey)
94-
throw new QiniuError(
96+
removeLocalFileInfo(localKey, logger)
97+
logger.warn(new QiniuError(
9598
QiniuErrorName.InvalidCacheData,
9699
`getLocalFileInfo failed to parse. key: ${localKey}`
97-
)
100+
))
98101
}
99102

100103
return localInfo
@@ -218,13 +221,14 @@ export function request<T>(url: string, options: RequestOptions): Response<T> {
218221
return
219222
}
220223

224+
const reqId = xhr.getResponseHeader('x-reqId') || ''
225+
221226
if (xhr.status === 0) {
222227
// 发生 0 基本都是网络错误,常见的比如跨域、断网、host 解析失败、系统拦截等等
223-
reject(new QiniuNetworkError('network error.'))
228+
reject(new QiniuNetworkError(reqId, 'network error.'))
224229
return
225230
}
226231

227-
const reqId = xhr.getResponseHeader('x-reqId') || ''
228232
if (xhr.status !== 200) {
229233
let message = `xhr request failed, code: ${xhr.status}`
230234
if (responseText) {

0 commit comments

Comments
 (0)