大文件上传--切片上传(前端:Vue,后端:Nestjs)---前端部分
前言
写网盘的项目的时候,需要文件上传功能,我使用的是nestjs框架做的后端,vue做的前端的一个全栈项目。 记录一下前后端大文件上传的详细知识。
这一篇是前端部分。
代码
前端
写秒传功能 思路:
- 秒传:前端需要切片读取这个文件的内容,并生成md5,后端根据这个是否有这个md5来判断是否上传,还是进行秒传。
- 切片上传:获取文件的大小,通过设置一个你想要切片的大小,然后通过计算算出需要切片的个数。之后判断是否后端有这个文件,有这个文件就是秒传,没有这个文件就是进行分片上传,当传完最后一个切片就进行合并文件。
秒传
//做秒传的,不做妙传的话可以不写,前端使用md5,后端通过这个md5来进行查询是否有这个md5
const computedMd5 = (fileItem: SingleFileStatus): any => {
let file = fileItem.file
let blobSlice = file.slice || file.mozSlice || file.webkitSlice
//进行切分,总的切片数目,应该向上取整
const chunks = Math.ceil(file.size / chunkSize)
let currentIndex = 0
const spark = new SparkMD5.ArrayBuffer()
//读取文件
const fileReader = new FileReader()
//进行加载下一部分file
const loadNext = () => {
//设置截取区间
let start = currentIndex * chunkSize
let end = start + chunkSize > file.size ? file.size : start + chunkSize
//进行截取
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
}
loadNext()
return new Promise((resolve, reject) => {
let resultFile = getFileById(file.uid) as SingleFileStatus
fileReader.onload = (e) => {
spark.append(e.target!.result)
//切片的增加
currentIndex++
if (currentIndex < chunks) {
console.log(`第${currentIndex}个切片完成`)
//计算进度
fileItem.md5Progress = Math.ceil((currentIndex / chunks) * 100)
//读取下一个
loadNext()
} else {
//最后一个切片,结束md5
let md5 = spark.end()
spark.destroy()
//进行设置文件状态
resultFile.md5Progress = 1
resultFile.md5 = md5
resultFile.status = STATUS.uploading.value
resolve(fileItem.uid)
}
}
fileReader.onerror = (err) => {
resultFile.md5Progress = -1
resultFile.status = STATUS.fail.value
reject(fileItem.uid)
}
}).catch((err) => {
console.log(err)
return null
})
}
上传其中有着文件状态的判断
//上传切片
const uploadChunk = async (uid: string | number, chunkIndex: number, fileHash: string) => {
chunkIndex = chunkIndex ? chunkIndex : 0
//寻找文件
let currentFile = getFileById(uid) as SingleFileStatus
//进行切片
const file = currentFile.file
const fileSize = currentFile.totalFileSize
const chunks = Math.ceil(fileSize / chunkSize)
const judjeFileStatus = async () => {
//判断文件状态
const verifyStatusParam: VerifyStatusParam = {
fileSize: file.size,
user_id: UserStore.userInfo._id,
fileHash: fileHash,
totalCount: chunks,
filename: file.name + '-' + chunkIndex,
file_type: file.type,
filePid: FileStore.filePid
}
const verifyStatus = await VerifyStatusApi(verifyStatusParam)
const data = verifyStatus.data
if (data.length === 0 || data === '') {
//秒传
currentFile.md5Progress = 100
currentFile.uploadProgress = 100
currentFile.status = STATUS.upload_seconds.value
//刷新重新获取文件
reload()
return 0
} else {
//可能是断点续传或者是一个新的上传
//上传中
currentFile.status = STATUS.uploading.value
return 1
}
}
const res = await judjeFileStatus()
if (res === 0) {
return
}
//分片上传
for (let i = chunkIndex; i < chunks; i++) {
//当前文件如果停止传输就不去传输了
if (currentFile.pause) {
break
}
//进行切分循环上传
const start = i * chunkSize
const end = start + chunkSize > fileSize ? fileSize + 1 : start + chunkSize
//需要上传的内容
const fileChunk = file.slice(start, end)
//上传内容
const ruleForm = {
chunkIndex: i,
fileHash: fileHash,
filename: file.name + '-' + i,
file_type: file.type
}
const otherForm = {
currentFile: currentFile,
fileSize: fileSize,
index: i,
chunkSize: chunkSize
}
const formData = new FormData()
formData.append('file', fileChunk)
await UploadChunkApi(formData, ruleForm, otherForm)
if (chunks === i - 1) {
currentFile.status = STATUS.upload_finish.value
}
}
//最后一次上传
if (
currentFile.status === STATUS.upload_seconds.value ||
currentFile.status === STATUS.upload_finish.value
) {
//设置进度为100
currentFile.uploadProgress = 100
//进行合并
const MergeParam: MergeParam = {
filename: file.name,
fileHash: fileHash,
fileSize: fileSize,
user_id: useUserInfo().userInfo._id,
file_type: file.type,
filePid: FileStore.filePid as string | number
}
const res = await MergeApi(MergeParam)
if (res.code === 0) {
//刷新重新获取文件
reload()
console.log(res, '合并成功了')
}
}
}
这一部分是寻找fileList中的文件
//根据id获取文件
const getFileById = (id: string | number) => {
return fileList.value.find((item) => {
return item.file.uid === id
})
}
转载自:https://juejin.cn/post/7357142305424703542