Skip to content

Commit 3ac68c7

Browse files
committed
添加 v1 分片上传支持
1 parent 81ad504 commit 3ac68c7

File tree

11 files changed

+445
-25
lines changed

11 files changed

+445
-25
lines changed

examples/wechat-miniprogram/pages/index/index.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ Component({
6262
await this.printTask(task)
6363
},
6464

65-
async uploadFile() {
65+
async uploadFileDirect() {
6666
const file: qiniu.FileData = {
6767
type: 'path',
6868
data: this.data.file,
@@ -73,7 +73,44 @@ Component({
7373
'223123231231': '2231232313'
7474
}
7575
}
76-
const task = qiniu.createMultipartUploadTask(file, {
76+
const task = qiniu.createDirectUploadTask(file, {
77+
vars: { 'test': '2222222' },
78+
tokenProvider: () => Promise.resolve(this.data.token)
79+
})
80+
await this.printTask(task)
81+
},
82+
83+
async uploadFileV1() {
84+
const file: qiniu.FileData = {
85+
type: 'path',
86+
data: this.data.file,
87+
filename: 'test.test2312',
88+
mimeType: 'test/test',
89+
metadata: {
90+
'222222123': '222231222',
91+
'223123231231': '2231232313'
92+
}
93+
}
94+
const task = qiniu.createMultipartUploadV1Task(file, {
95+
// logLevel: "INFO",
96+
vars: { 'test': '2222222' },
97+
tokenProvider: () => Promise.resolve(this.data.token)
98+
})
99+
await this.printTask(task)
100+
},
101+
102+
async uploadFileV2() {
103+
const file: qiniu.FileData = {
104+
type: 'path',
105+
data: this.data.file,
106+
filename: 'test.test2312',
107+
mimeType: 'test/test',
108+
metadata: {
109+
'222222123': '222231222',
110+
'223123231231': '2231232313'
111+
}
112+
}
113+
const task = qiniu.createMultipartUploadV2Task(file, {
77114
vars: { 'test': '2222222' },
78115
tokenProvider: () => Promise.resolve(this.data.token)
79116
})

examples/wechat-miniprogram/pages/index/index.wxml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
<button class="button" bind:tap="uploadArrayBuffer">ArrayBuffer 上传测试</button>
88
<textarea class="textarea" model:value="{{file}}"></textarea>
99
<button class="button" bind:tap="selectMedia">选择文件</button>
10-
<button class="button" bind:tap="uploadFile">文件上传测试</button>
10+
<button class="button" bind:tap="uploadFileDirect">直传</button>
11+
<button class="button" bind:tap="uploadFileV1">分片 v1</button>
12+
<button class="button" bind:tap="uploadFileV2">分片 v2</button>
1113
<button class="button" bind:tap="stopTask">停止上传</button>
1214
<button class="button" bind:tap="startTask">继续上传</button>
1315
</view>

packages/browser/src/index.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,20 @@ export const createDirectUploadTask = (file: FileData, config: common.UploadConf
2727
return task
2828
}
2929

30-
export const createMultipartUploadTask = (file: FileData, config: common.UploadConfig) => {
30+
export const createMultipartUploadV1Task = (file: FileData, config: common.UploadConfig) => {
3131
const innerFile = new UploadFile(file)
3232
config.httpClient = config.httpClient ?? new HttpClient()
33-
const task = common.createMultipartUploadTask(innerFile, config)
33+
const task = common.createMultipartUploadV1Task(innerFile, config)
34+
task.onError(() => innerFile.free())
35+
task.onComplete(() => innerFile.free())
36+
afterCancel(task, () => innerFile.free())
37+
return task
38+
}
39+
40+
export const createMultipartUploadV2Task = (file: FileData, config: common.UploadConfig) => {
41+
const innerFile = new UploadFile(file)
42+
config.httpClient = config.httpClient ?? new HttpClient()
43+
const task = common.createMultipartUploadV2Task(innerFile, config)
3444
task.onError(() => innerFile.free())
3545
task.onComplete(() => innerFile.free())
3646
afterCancel(task, () => innerFile.free())

packages/common/src/api/index.ts

Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ interface UploadPartParams extends BasicWithAuthParams {
3333
key?: string
3434
}
3535

36-
interface ListMultipartUploadPartsParams extends BasicWithAuthParams {
36+
interface ListPartsParams extends BasicWithAuthParams {
3737
uploadId: string
3838
bucket: string
3939
key?: string
@@ -44,7 +44,7 @@ export interface UploadedPart extends PartMeta {
4444
putTime: number
4545
}
4646

47-
interface ListUploadPartsData {
47+
interface ListPartsData {
4848
uploadId: string
4949
expireAt: number
5050
partNumberMarker: number
@@ -87,6 +87,29 @@ interface GetChunkRequestPathParams extends BasicWithAuthParams {
8787
key?: string
8888
}
8989

90+
interface MkblkParams extends BasicWithAuthParams {
91+
// 第一个块的二进制内容
92+
firstChunkBinary: UploadBlob
93+
}
94+
95+
export interface MkblkData {
96+
ctx: string
97+
checksum: string
98+
crc32: number
99+
offset: number
100+
host: string
101+
expired_at: number
102+
}
103+
104+
interface MkfileParams extends BasicWithAuthParams {
105+
fileSize: number // 资源文件大小,单位字节。
106+
key?: string // 进行URL 安全的 Base64 编码后的资源名。若未指定,则使用saveKey;若未指定 saveKey,则使用资源内容的 SHA1 值作为资源名。
107+
fname?: string // 进行URL安全的Base64编码后的文件名称。若未指定,则魔法变量中无法使用fname, ext, fprefix。
108+
mimeType?: string // 进行URL 安全的 Base64 编码后的文件 mimeType。若未指定,则根据文件内容自动检测 mimeType。
109+
userVars?: Record<string, string> // 指定自定义变量,进行URL 安全的 Base64 编码后的 user-var。
110+
lastCtxOfBlock: string[]
111+
}
112+
90113
interface DirectUploadParams extends BasicWithAuthParams {
91114
file: UploadFile
92115
fileName: string
@@ -174,6 +197,8 @@ export class UploadApis {
174197
return parseResponseJson<InitPartsUploadData>(response.result)
175198
}
176199

200+
// v2 接口
201+
177202
async uploadPart(params: UploadPartParams): Promise<Result<UploadChunkData>> {
178203
const requestPathResult = await this.getBaseRequestPath(params)
179204
if (!isSuccessResult(requestPathResult)) return requestPathResult
@@ -201,7 +226,7 @@ export class UploadApis {
201226
return parseResponseJson<UploadChunkData>(response.result)
202227
}
203228

204-
async listUploadParts(params: ListMultipartUploadPartsParams): Promise<Result<ListUploadPartsData>> {
229+
async listParts(params: ListPartsParams): Promise<Result<ListPartsData>> {
205230
const requestPathResult = await this.getBaseRequestPath(params)
206231
if (!isSuccessResult(requestPathResult)) return requestPathResult
207232

@@ -222,7 +247,7 @@ export class UploadApis {
222247
return handleResponseError(response.result)
223248
}
224249

225-
return parseResponseJson<ListUploadPartsData>(response.result)
250+
return parseResponseJson<ListPartsData>(response.result)
226251
}
227252

228253
async completeMultipartUpload(params: CompleteMultipartUploadParams): Promise<Result<string>> {
@@ -296,6 +321,76 @@ export class UploadApis {
296321
return { result: response.result.data }
297322
}
298323

324+
// v1 接口
325+
326+
async mkblk(params: MkblkParams): Promise<Result<MkblkData>> {
327+
const blobSize = params.firstChunkBinary.size()
328+
const url = `${params.uploadHostUrl}/mkblk/${blobSize}`
329+
const headers = this.generateAuthHeaders(params.token.signature)
330+
headers['content-type'] = 'application/octet-stream'
331+
332+
const response = await this.httpClient.post(url, {
333+
headers,
334+
abort: params.abort,
335+
body: params.firstChunkBinary,
336+
onProgress: params.onProgress
337+
})
338+
339+
if (!isSuccessResult(response)) {
340+
return response
341+
}
342+
343+
if (response.result.code !== 200) {
344+
return handleResponseError(response.result)
345+
}
346+
347+
return parseResponseJson<MkblkData>(response.result)
348+
}
349+
350+
async mkfile(params: MkfileParams): Promise<Result<string>> {
351+
let url = `${params.uploadHostUrl}/mkfile/${params.fileSize}`
352+
353+
if (params.key) {
354+
url += `/key/${urlSafeBase64Encode(params.key)}`
355+
}
356+
357+
if (params.fname) {
358+
url += `/fname/${urlSafeBase64Encode(params.fname)}`
359+
}
360+
361+
if (params.mimeType) {
362+
url += `/mimeType/${urlSafeBase64Encode(params.mimeType)}`
363+
}
364+
365+
if (params.userVars && Array.isArray(params.userVars)) {
366+
for (const [key, value] of Object.entries(params.userVars)) {
367+
url += `/x:${key}/${value}`
368+
}
369+
}
370+
371+
const headers = this.generateAuthHeaders(params.token.signature)
372+
headers['content-type'] = 'text/plain'
373+
374+
const response = await this.httpClient.post(url, {
375+
headers,
376+
abort: params.abort,
377+
onProgress: params.onProgress,
378+
body: params.lastCtxOfBlock.join(',')
379+
})
380+
381+
if (!isSuccessResult(response)) {
382+
return response
383+
}
384+
385+
if (response.result.code !== 200) {
386+
return handleResponseError(response.result)
387+
}
388+
389+
return { result: response.result.data }
390+
}
391+
392+
// 直传接口
393+
299394
async directUpload<T>(params: DirectUploadParams): Promise<Result<string>> {
300395
const formData = new HttpFormData()
301396
formData.set('token', params.token.signature)

packages/common/src/upload/common/queue/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export class TaskQueue {
6666

6767
constructor(private options?: TaskQueueOptions) {
6868
this.tasksCreator = options?.tasksCreator
69+
this.concurrentTicket = options?.concurrentLimit || 1
6970
this.logger = new Logger(options?.logger?.level, options?.logger?.prefix)
7071
}
7172

packages/common/src/upload/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { UploadConfig, UploadTask, UploadTaskCreator, Progress } from './types'
2-
export { createMultipartUploadTask, MultipartUploadContext } from './multipart'
2+
export { createMultipartUploadV1Task, MultipartUploadV1Context } from './multipartv1'
3+
export { createMultipartUploadV2Task, MultipartUploadV2Context } from './multipartv2'
34
export { createDirectUploadTask, DirectUploadContext } from './direct'

0 commit comments

Comments
 (0)