题引
利用 Blob.prototype.slice方法进行切片
可以安装这些依赖包
"dependencies": {
"crypto-js": "^4.0.0",
"goog": "^0.2.1",
"google-protobuf": "^3.14.0-rc.2",
"grpc-web": "^1.2.1",
"lodash": "^4.17.20"
}
参数设置
类名:BigFileUpload
设置配置项
BigFileUpload.prototype.options = {
url: "",
chunkSize: 10 * 1024 * 1024, //默认单个切片大小:10M
concurrency: 10, // 一次最多上传10个,防止大量切片同时上传
}
设置上传事件,便于页面调用过程中监听
BigFileUpload.prototype.events = ["error", "progress", "cancel", "success"]
grpc连接
引入相关数据,HelloWorld_pb中是相关参数的数据结构,DemoClient是grpc服务的包装类
import {
UploadPartRequest,
UserAuthorization,
FileDescriptor
} from "./HelloWorld_pb"
import {
DemoService
} from "./HelloWorld_grpc_web_pb"
服务连接
client = new DemoService(options.url, null, null)
上传
接收文件时,构造一个文件对象
chunkNum = Math.ceil(file.size / options.chunkSize)
for (let i = 0; i < chunkNum; i++) {
notUploadedPart.push(i)
}
fileOb = {
path: path,
fileName: file.name,
fileSize: file.size,
chunkNum: chunkNum,
uploadPercentage: 0,
uploadStatus: "uploading", //[uploading,error,success,-----]
file: file,
uploadedPart: [],
notUploadedPart: notUploadedPart,
runningPart: []
}
上传单个分片
uploadChunk: function (i) {
let end =
(i + 1) * options.chunkSize >= fileOb.fileSize ?
fileOb.fileSize :
(i + 1) * options.chunkSize,
request = new UploadPartRequest(),
reader = new FileReader()
request.setPartOrder(i + 1)
reader.onload = function () {
request.setBody(new Uint8Array(reader.result))
client.mpuUploadPart(request, null, (err, response) => {
if (fileOb.uploadStatus === "aborted" || fileOb.uploadStatus === "error") {
return
}
fileOb.runningPart.splice(fileOb.runningPart.indexOf(i), 1)
if (err) {
self.postMessage({
type: "error",
content: “*****”
})
}
} else {
fileOb.uploadedPart.push(i)
//计算百分比
let percentage = (fileOb.uploadedPart.length / fileOb.chunkNum).toFixed(2)
fileOb.uploadPercentage = percentage
self.postMessage({
type: "progress",
content: {
percentage: percentage
}
})
setUploadQueue()
}
})
}
reader.onerror = function (err) {
fileOb.uploadStatus = "error"
self.postMessage({
type: "error",
content: “****”
})
}
//文件切片,并转换成包含一个 ArrayBuffer 对象以表示所读取文件的数据
reader.readAsArrayBuffer(fileOb.file.slice(i * options.chunkSize, end))
},
//并发队列
setUploadQueue: function () {
if (fileOb.uploadedPart.length === fileOb.chunkNum) {
fileOb.uploadStatus = "success"
self.postMessage({
type: "success",
content: {
fileOb: fileOb,
}
})
} else {
while (fileOb.uploadedPart.length < fileOb.chunkNum && fileOb.notUploadedPart.length > 0 && fileOb.runningPart.length < options.concurrency) {
const index = fileOb.notUploadedPart.shift()
uploadChunk(index)
fileOb.runningPart.push(index)
}
}
},
总结
大文件上传通常会有秒传这个要求,下章将解决这个问题