特别提醒
大文件上传的文件切片逻辑, 包括如何将分片文件上传到本地服务器, 请查看之前的文章: 前端大文件上传 - 总结(Vue3 + hook + Web Worker实现,通过多个Worker线程大大提高Hash计算的速度), 本篇仅实现如何将大文件分块上传到S3.
后面写一篇文章实现选中多个文件,显示上传列表可取消上传,以及整合这两篇文件到一篇里,因为涉及的逻辑比较多
流程图
实现逻辑
上节的代码实现中的 uploadFile 函数,我们只需要在对应的代码中添加 加粗样式就行
// 例如下面
async function uploadFile() {const data = await checkFile()if (!data) returnconst { chunk_upload, upload_id } = datauploadId.value = upload_idif (chunk_upload.length === 0) {// 上传整个文件// 上传到S3if (isS3) return await handleUploadS3Request()}if (chunk_upload.length !== chunkTotal.value) {// 上传未上传的分片 - 断点续传if (isS3) return await handleUploadS3Request(chunk_upload)}// 无论是上传到S3还是本地服务器可能存在合并失败的问题// 这里应该处理未合并的情况,值需要发送合并请求code...// 上传完成 - 秒传(可能需要发起合并请求)code...
}
handleUploadS3Request函数
提醒: 获取ETag需要配置对应的桶策略,ExposeHeaders数组里配置ETag。
// 记录上传到S3的分块const s3UploadedChunks = ref([])// 上传到S3async function handleUploadS3Request(uploadedChunks = []) {s3UploadedChunks.value = JSON.parse(JSON.stringify(uploadedChunks))for (let i = 0; i < fileChunkList.value.length; i++) {if (uploadedChunks.indexOf(i + 1) === -1) {// 申请上传S3的urlconst { code, data, msg } = await applyS3UrlFn({part_no: i + 1,upload_id: uploadId.value,})if (code === 0) {try {await uploadS3Chunk(data.signed_url, i)} catch (error) {// 上传失败...return false}} else {// 申请失败...return false}}}}// 上传S3文件某个分块async function uploadS3Chunk(url, i) {const etag = await createXhr(url, i)// 记录已上传的分块s3UploadedChunks.value.push(i + 1)// 上传S3文件某个分块完成await s3ChunkDoneFn({part_no: i + 1,upload_id: uploadId.value,etag,})if (s3UploadedChunks.value.length === chunkTotal.value) {// 上传S3文件所有分块完成await s3AllChunkDoneFn({ upload_id: uploadId.value })// 上传成功callback && callback()}}let xhr = null// 使用XMLHttpRequest上传Chunkasync function createXhr(url, i) {return new Promise((resolve, reject) => {xhr = new XMLHttpRequest()xhr.open('PUT', url)xhr.upload.onprogress = (e) => {getFileProgress(e, i)}xhr.onload = () => {if (xhr.status === 200) {// 返回ETagresolve(xhr.getResponseHeader('ETag'))} else {reject(new Error('File upload failed'))}}xhr.onerror = () => {reject(new Error('File upload failed'))}xhr.onabort = () => {cancelFn({ upload_id: uploadId.value })reject(new Error('File upload aborted'))}xhr.send(fileChunkList.value[i])})}