Skip to content

Commit ac8f70c

Browse files
committed
类型优化
1 parent 5fe655a commit ac8f70c

File tree

11 files changed

+62
-81
lines changed

11 files changed

+62
-81
lines changed

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,17 @@ qiniu.compressImage(file, options).then(data => {
173173
* total.total: `number`,本次上传的总量控制信息,单位为字节,注意这里的 total 跟文件大小并不一致。
174174
* total.percent: `number`,当前上传进度,范围:0100
175175

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

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

src/api/index.mock.ts

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

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

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

src/api/index.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ describe('api function test', () => {
6161
url = await getUploadUrl(config, token)
6262
expect(url).toBe('http://upload.qiniup.com')
6363

64-
6564
config.uphost = 'qiniu.com'
6665
url = await getUploadUrl(config, token)
6766
expect(url).toBe('http://qiniu.com')

src/api/index.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { stringify } from 'querystring'
22

33
import { normalizeUploadConfig } from '../utils'
4-
import { Config, UploadInfo } from '../upload'
5-
import { regionUphostMap } from '../config'
4+
import { Config, InternalConfig, UploadInfo } from '../upload'
65
import * as utils from '../utils'
76

87
interface UpHosts {
@@ -16,14 +15,12 @@ interface UpHosts {
1615
}
1716
}
1817

19-
export async function getUpHosts(accessKey: string, bucketName: string, protocol: Config['upprotocol']): Promise<UpHosts> {
18+
export async function getUpHosts(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol']): Promise<UpHosts> {
2019
const params = stringify({ ak: accessKey, bucket: bucketName })
2120
const url = `${protocol}://api.qiniu.com/v2/query?${params}`
2221
return utils.request(url, { method: 'GET' })
2322
}
2423

25-
export type UploadUrlConfig = Partial<Pick<Config, 'upprotocol' | 'uphost' | 'region' | 'useCdnDomain'>>
26-
2724
/**
2825
* @param bucket 空间名
2926
* @param key 目标文件名
@@ -153,6 +150,8 @@ export function direct(
153150
})
154151
}
155152

153+
export type UploadUrlConfig = Partial<Pick<Config, 'upprotocol' | 'uphost' | 'region' | 'useCdnDomain'>>
154+
156155
/**
157156
* @param {UploadUrlConfig} config
158157
* @param {string} token
@@ -166,13 +165,6 @@ export async function getUploadUrl(_config: UploadUrlConfig, token: string): Pro
166165
if (config.uphost.length > 0) {
167166
return `${protocol}://${config.uphost[0]}`
168167
}
169-
170-
if (config.region) {
171-
const upHosts = regionUphostMap[config.region]
172-
const host = config.useCdnDomain ? upHosts.cdnUphost[0] : upHosts.srcUphost[0]
173-
return `${protocol}://${host}`
174-
}
175-
176168
const putPolicy = utils.getPutPolicy(token)
177169
const res = await getUpHosts(putPolicy.assessKey, putPolicy.bucketName, protocol)
178170
const hosts = res.data.up.acc.main

src/errors/index.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,7 @@ export enum QiniuErrorName {
2424
InvalidProgressEventTarget = 'InvalidProgressEventTarget',
2525

2626
// 请求错误
27-
RequestError = 'RequestError',
28-
29-
// 网络错误
30-
NetworkError = 'NetworkError'
27+
RequestError = 'RequestError'
3128
}
3229

3330
export class QiniuError implements Error {
@@ -49,3 +46,9 @@ export class QiniuRequestError extends QiniuError {
4946
super(QiniuErrorName.RequestError, message)
5047
}
5148
}
49+
50+
export class QiniuNetworkError extends QiniuRequestError {
51+
constructor(message: string) {
52+
super(0, '', message)
53+
}
54+
}

src/upload/base.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ export type XHRHandler = (xhr: XMLHttpRequest) => void
9898
const GB = 1024 ** 3
9999

100100
export default abstract class Base {
101-
protected config: Config
101+
protected config: InternalConfig
102102
protected putExtra: Extra
103103

104104
protected aborted = false

src/upload/hosts.ts

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { getUpHosts } from '../api'
2-
import { Config } from './base'
2+
import { InternalConfig } from './base'
33

44
/**
55
* @description 解冻时间,key 是 host,value 为解冻时间
66
*/
77
const unfreezeTimeMap = new Map<string, number>()
88

99
export class Host {
10-
constructor(public host: string, public protocol: Config['upprotocol'] = 'https') { }
10+
constructor(public host: string, public protocol: InternalConfig['upprotocol']) { }
1111

1212
/**
1313
* @description 当前 host 是否为冻结状态
@@ -49,11 +49,6 @@ export class Host {
4949
}
5050
}
5151
export class HostPool {
52-
/**
53-
* @description 构造类时传入的数据
54-
*/
55-
private hosts?: string[]
56-
5752
/**
5853
* @description 缓存的 host 表,以 bucket 和 accessKey 作为 key
5954
*/
@@ -63,21 +58,18 @@ export class HostPool {
6358
* @param {string[]} hosts
6459
* @description 如果在构造时传入 hosts,则该 host 池始终使用传入的 host 做为可用的数据
6560
*/
66-
constructor(hosts?: string[]) {
67-
if (hosts != null) {
68-
this.hosts = hosts
69-
}
61+
constructor(private hosts?: string[]) {
7062
}
7163

7264
/**
7365
* @param {string} accessKey
7466
* @param {string} bucketName
7567
* @param {string[]} hosts
76-
* @param {Config['upprotocol']} protocol
68+
* @param {InternalConfig['upprotocol']} protocol
7769
* @returns {void}
7870
* @description 注册可用 host
7971
*/
80-
private register(accessKey: string, bucketName: string, hosts: string[], protocol: Config['upprotocol']): void {
72+
private register(accessKey: string, bucketName: string, hosts: string[], protocol: InternalConfig['upprotocol']): void {
8173
this.cachedHostsMap.set(
8274
`${accessKey}@${bucketName}`,
8375
hosts.map(host => new Host(host, protocol))
@@ -87,11 +79,11 @@ export class HostPool {
8779
/**
8880
* @param {string} accessKey
8981
* @param {string} bucketName
90-
* @param {Config['upprotocol']} protocol
82+
* @param {InternalConfig['upprotocol']} protocol
9183
* @returns {Promise<void>}
9284
* @description 从接口刷新最新的 host 数据,如果用户在构造时该类时传入了 host、则不会发起请求、而是固定使用传入的数据。
9385
*/
94-
private async refresh(accessKey: string, bucketName: string, protocol: Config['upprotocol']): Promise<void> {
86+
private async refresh(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol']): Promise<void> {
9587
const hosts: string[] = []
9688
if (this.hosts != null && this.hosts.length > 0) {
9789
hosts.push(...this.hosts)
@@ -111,19 +103,20 @@ export class HostPool {
111103
/**
112104
* @param {string} accessKey
113105
* @param {string} bucketName
114-
* @param {Config['upprotocol']} protocol
106+
* @param {InternalConfig['upprotocol']} protocol
115107
* @param {boolean} isRefresh
116108
* @returns {Promise<Host | null>}
117109
* @description 获取一个可用的上传 Host,排除已冻结的
118110
*/
119-
public async getUp(accessKey: string, bucketName: string, protocol: Config['upprotocol'], isRefresh = false): Promise<Host | null> {
111+
public async getUp(accessKey: string, bucketName: string, protocol: InternalConfig['upprotocol'], isRefresh = false): Promise<Host | null> {
120112
if (isRefresh) await this.refresh(accessKey, bucketName, protocol)
121113

122114
const cachedHostList = this.cachedHostsMap.get(`${accessKey}@${bucketName}`) || []
123115
if (cachedHostList.length === 0 && !isRefresh) {
124116
return this.getUp(accessKey, bucketName, protocol, true)
125117
}
126118

119+
if (cachedHostList.length === 0) return null
127120
const availableHostList = cachedHostList.filter(host => !host.isFrozen())
128121
if (availableHostList.length > 0) return availableHostList[0]
129122

src/upload/index.test.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,19 +47,12 @@ describe('test upload', () => {
4747
})
4848

4949
test('Direct: all api error state.', async () => {
50-
const testStateTable: Array<keyof typeof errorMap> = [
51-
'invalidParams', 'expiredToken',
52-
'gatewayUnavailable', 'serviceUnavailable',
53-
'serviceTimeout', 'serviceError',
54-
'invalidUploadId', 'networkError'
55-
]
56-
57-
for (const state of testStateTable) {
50+
for (const error of Object.values(errorMap)) {
5851
localStorage.clear()
5952
mockApi.clearInterceptor()
60-
mockApi.setInterceptor('direct', () => Promise.reject(errorMap[state]))
53+
mockApi.setInterceptor('direct', () => Promise.reject(error))
6154
await expect(observablePromisify(upload(File3M, null, testToken)))
62-
.rejects.toStrictEqual(errorMap[state])
55+
.rejects.toStrictEqual(error)
6356
}
6457
})
6558

@@ -75,20 +68,13 @@ describe('test upload', () => {
7568
'uploadChunk', 'uploadComplete'
7669
]
7770

78-
const testStateTable: Array<keyof typeof errorMap> = [
79-
'invalidParams', 'expiredToken',
80-
'gatewayUnavailable', 'serviceUnavailable',
81-
'serviceTimeout', 'serviceError',
82-
'invalidUploadId', 'networkError'
83-
]
84-
8571
for (const apiName of testApiTable) {
86-
for (const state of testStateTable) {
72+
for (const error of Object.values(errorMap)) {
8773
localStorage.clear()
8874
mockApi.clearInterceptor()
89-
mockApi.setInterceptor(apiName, (..._: any[]) => Promise.reject(errorMap[state]))
75+
mockApi.setInterceptor(apiName, (..._: any[]) => Promise.reject(error))
9076
await expect(observablePromisify(upload(File5M, null, testToken)))
91-
.rejects.toStrictEqual(errorMap[state])
77+
.rejects.toStrictEqual(error)
9278
}
9379
}
9480
})

src/upload/index.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,7 @@ import Logger from '../logger'
44
import { QiniuError } from '../errors'
55
import { UploadCompleteData } from '../api'
66
import { Observable, IObserver, MB, normalizeUploadConfig } from '../utils'
7-
8-
import {
9-
Config, Extra, UploadOptions,
10-
UploadHandlers, UploadProgress
11-
} from './base'
7+
import { Extra, UploadOptions, UploadHandlers, UploadProgress, InternalConfig } from './base'
128
import { HostPool } from './hosts'
139

1410
export * from './base'
@@ -47,23 +43,23 @@ export default function upload(
4743
key: string | null | undefined,
4844
token: string,
4945
putExtra?: Partial<Extra>,
50-
config?: Partial<Config>
46+
config?: Partial<InternalConfig>
5147
): Observable<UploadProgress, QiniuError, UploadCompleteData> {
5248

49+
// 为每个任务创建单独的 Logger
50+
const logger = new Logger(token, config?.disableStatisticsReport, config?.debugLogLevel, file.name)
51+
5352
const options: UploadOptions = {
5453
file,
5554
key,
5655
token,
5756
putExtra,
58-
config: normalizeUploadConfig(config)
57+
config: normalizeUploadConfig(config, logger)
5958
}
6059

6160
// 创建 host 池
6261
const hostPool = new HostPool(options.config?.uphost)
6362

64-
// 为每个任务创建单独的 Logger
65-
const logger = new Logger(options.token, config?.disableStatisticsReport, config?.debugLogLevel, file.name)
66-
6763
return new Observable((observer: IObserver<UploadProgress, QiniuError, UploadCompleteData>) => {
6864
const manager = createUploadManager(options, {
6965
onData: (data: UploadProgress) => observer.next(data),

src/utils/config.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import Logger from '../logger'
12
import { regionUphostMap } from '../config'
23
import { Config, DEFAULT_CHUNK_SIZE, InternalConfig } from '../upload'
34

4-
export function normalizeUploadConfig(config?: Partial<Config>): InternalConfig {
5+
export function normalizeUploadConfig(config?: Partial<Config>, logger?: Logger,): InternalConfig {
56
const { upprotocol, uphost, ...otherConfig } = { ...config }
67

78
const normalizeConfig: InternalConfig = {
@@ -23,14 +24,19 @@ export function normalizeUploadConfig(config?: Partial<Config>): InternalConfig
2324
}
2425

2526
// 兼容原来的 http: https: 的写法
26-
normalizeConfig.upprotocol = upprotocol
27-
? upprotocol.replace(/:$/, '') as InternalConfig['upprotocol']
28-
: normalizeConfig.upprotocol
27+
if (upprotocol) {
28+
normalizeConfig.upprotocol = upprotocol
29+
.replace(/:$/, '') as InternalConfig['upprotocol']
30+
}
2931

3032
const hostList: string[] = []
3133

34+
if (logger && config?.uphost != null && config?.region != null) {
35+
logger.warn('do not use both the uphost and region config.')
36+
}
37+
3238
// 如果同时指定了 uphost 参数,添加到可用 host 列表
33-
if (uphost != null) {
39+
if (uphost) {
3440
if (Array.isArray(uphost)) {
3541
hostList.push(...uphost)
3642
} else {

src/utils/helper.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import SparkMD5 from 'spark-md5'
22
import { urlSafeBase64Decode } from './base64'
33
import { Progress, LocalInfo } from '../upload'
4-
import { QiniuErrorName, QiniuError, QiniuRequestError } from '../errors'
4+
import { QiniuErrorName, QiniuError, QiniuRequestError, QiniuNetworkError } from '../errors'
55

66
export const MB = 1024 ** 2
77

@@ -220,7 +220,7 @@ export function request<T>(url: string, options: RequestOptions): Response<T> {
220220

221221
if (xhr.status === 0) {
222222
// 发生 0 基本都是网络错误,常见的比如跨域、断网、host 解析失败、系统拦截等等
223-
reject(new QiniuError(QiniuErrorName.NetworkError, 'network error.'))
223+
reject(new QiniuNetworkError('network error.'))
224224
return
225225
}
226226

@@ -248,7 +248,7 @@ export function request<T>(url: string, options: RequestOptions): Response<T> {
248248
})
249249
}
250250

251-
export function getPortFromUrl(url?: string) {
251+
export function getPortFromUrl(url: string | undefined) {
252252
if (url && url.match) {
253253
let groups = url.match(/(^https?)/)
254254

@@ -273,7 +273,7 @@ export function getPortFromUrl(url?: string) {
273273
return ''
274274
}
275275

276-
export function getDomainFromUrl(url?: string): string {
276+
export function getDomainFromUrl(url: string | undefined): string {
277277
if (url && url.match) {
278278
const groups = url.match(/^https?:\/\/([^:^/]*)/)
279279
return groups ? groups[1] : ''

0 commit comments

Comments
 (0)