前端大文件上传 -- 上传文件到S3或本地服务器

特别提醒

大文件上传的文件切片逻辑, 包括如何将分片文件上传到本地服务器, 请查看之前的文章: 前端大文件上传 - 总结(Vue3 + hook + Web Worker实现,通过多个Worker线程大大提高Hash计算的速度), 本篇仅实现如何将大文件分块上传到S3.
后面写一篇文章实现选中多个文件,显示上传列表可取消上传,以及整合这两篇文件到一篇里,因为涉及的逻辑比较多

流程图

请添加图片描述

实现逻辑

上节的代码实现中的 uploadFile 函数,我们只需要在对应的代码中添加 加粗样式就行

// 例如下面
async function uploadFile() {
	const data = await checkFile()
	if (!data) return

	const { chunk_upload, upload_id } = data
	uploadId.value = upload_id

	if (chunk_upload.length === 0) {
		// 上传整个文件
		// 上传到S3
		if (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([])

// 上传到S3
	async 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的url
				const { 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上传Chunk
	async 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) {
					// 返回ETag
					resolve(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])
		})
	}

相关推荐

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-14 16:42:05       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-14 16:42:05       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-14 16:42:05       82 阅读
  4. Python语言-面向对象

    2024-04-14 16:42:05       91 阅读

热门阅读

  1. 前端面试问题汇总 - Vue篇

    2024-04-14 16:42:05       31 阅读
  2. 将基于Centos下的Linux 中的man 汉化

    2024-04-14 16:42:05       37 阅读
  3. 学术写作进阶:ChatGPT辅助下的论文撰写技巧

    2024-04-14 16:42:05       36 阅读
  4. ARM-SC2440

    2024-04-14 16:42:05       33 阅读
  5. npm 常用命令详解

    2024-04-14 16:42:05       41 阅读
  6. 每天一个数据分析题(二百六十二)

    2024-04-14 16:42:05       43 阅读
  7. 每天一个数据分析题(二百六十一)

    2024-04-14 16:42:05       39 阅读
  8. 算法与数据结构 顺序栈(C++)

    2024-04-14 16:42:05       32 阅读
  9. 【SQL】mysql函数列表

    2024-04-14 16:42:05       40 阅读