<template>
<div>
<a-upload
:accept="accept"
:beforeUpload="beforeUpload"
:disabled="disabled"
:fileList="fileList"
:list-type="listType"
:multiple="true"
:remove="handleImageRemove"
@change="handleChange"
>
<a-button :disabled="disabled">
<a-icon type="upload" />
{{ text }}
</a-button>
</a-upload>
</div>
</template>
<script>
import md5 from "js-md5"
import {
getCheckFile,
getChunkFile,
getUploadPart,
getMergeFile,
getCheckCosFile,
getChunkCosFile,
getCosUploadPart,
getMergeCosFile
} from "@/api/common"
export default {
name: "ChuckUploadComponent",
props: {
isCosType: { type: Boolean, required: false, default: false },
disabled: { type: Boolean, required: false, default: false },
count: { type: Number, required: false, default: 1 },
uploadLists: {
type: Array,
required: false,
default: () => {
return []
}
},
listType: { type: String, required: false, default: "text" },
text: { type: String, required: false, default: "选择文件" },
accept: {
type: String,
required: false,
default: "*"
},
ext: {
type: Array,
required: false,
default: () => {
return []
}
}
},
data() {
return {
fileList: [],
tempThreads: 5,
chunkRetry: 4,
chunkSize: 5 * 1024 * 1024
}
},
created() {},
watch: {
uploadLists(value, oldValue) {
this.fileList = value
}
},
methods: {
beforeUpload(file) {
this.fileList = [...this.fileList, file]
file.status = "uploading"
this.checkFile(file)
return false
},
// 判断文件是否上传过,获取fileId
checkFile(file) {
const md5 = this.getFileMD5(file)
file.md5 = md5
const formData = new FormData()
formData.append("fileMd5", md5)
formData.append("fileName", file.name)
if (this.isCosType) {
formData.append("fileSize", file.size)
}
const http = this.isCosType
? getCheckCosFile(formData)
: getCheckFile(formData)
http.then(async res => {
if (res.code === 0) {
if (res.data.status === 1) {
// 文件存在
if (res.data.fileId) {
file.fileId = res.data.fileId
this.fileList.forEach(item => {
if (item.md5 === md5) {
item.status = "done"
this.$emit("fileId", res.data.fileId)
this.$message.success(item.name + " 文件上传成功!")
}
})
} else {
// console.log(`fileId不存在`)
this.$message.error(res.msg)
}
} else {
// 文件不存在或不完整,发送该文件
await this.uploadChunks(file)
}
}
})
},
// 根据文件大小,分配上传分片大小
updateCchunkSize(file) {
if (file.size > 2000 * 1024 * 1024) {
this.chunkSize = 1024 * 1024 * 15
} else if (file.size > 1000 * 1024 * 1024) {
this.chunkSize = 1024 * 1024 * 10
} else if (file.size > 500 * 1024 * 1024) {
this.chunkSize = 1024 * 1024 * 8
} else {
this.chunkSize = 2 * 1024 * 1024
}
},
createFileChunk(file, size) {
const fileChunkList = []
let count = 0
while (count < file.size) {
fileChunkList.push({
file: file.slice(count, count + size)
})
count += size
}
return fileChunkList
},
async uploadChunks(file) {
this.updateCchunkSize(file)
const fileChunkList = this.createFileChunk(file, this.chunkSize)
file.chunkList = fileChunkList.map(({ file }, index) => ({
index,
source: file,
size: file.size
}))
var chunkData = file.chunkList
return new Promise((resolve, reject) => {
const requestDataList = chunkData.map(value => {
const formData = new FormData()
formData.append("fileMd5", file.md5)
formData.append("chunk", value.index)
formData.append("file", value.source)
return { formData, index: value.index, md5: file.md5, file }
})
try {
const ret = this.sendRequest(requestDataList)
// console.log('上传结束,参数:', ret, chunkData, file.md5)
resolve(ret)
} catch (error) {
this.$message.error("上传失败,请重试")
reject("sendRequest 失败", error)
}
}).then(async res => {
if (res == file.md5) {
// console.log('--- ' + file.name + ' 文件开始合并----')
await this.mergeRequest(file.md5)
}
})
},
// 并发,重试请求
async sendRequest(list) {
var finished = 0
const retryArr = []
var currentFileInfo
const total = list.length
// 所有请求都存放这个promise中
return new Promise((resolve, reject) => {
const handler = () => {
if (list.length) {
const formInfo = list.shift()
const index = formInfo.index
const http = this.isCosType
? getChunkCosFile(formInfo.formData)
: getChunkFile(formInfo.formData)
http
.then(res => {
// if (res.code === 0) {
// //分片存在,跳过. 待测试
// finished++
// handler()
// } else {
const http = this.isCosType
? getCosUploadPart(formInfo.formData)
: getUploadPart(formInfo.formData)
// 分块不存在或不完整,重新发送该分块内容
http
.then(res => {
if (res.code === 0) {
// const date = new Date()
// console.log(formInfo.file.name + 'UploadPart', formInfo, date.getTime(), res)
currentFileInfo = formInfo
}
return res
})
.then(res => {
if (res.code === 0) {
finished++
handler()
} else {
this.$message.error(res.msg)
}
})
.catch(e => {
if (typeof retryArr[index] !== "number") {
retryArr[index] = 1
}
if (retryArr[index] >= this.chunkRetry) {
return reject("重试失败", retryArr)
}
// console.log(`${formInfo.file.name}--文件的第 ${index} 个分块,开始进行第 ${retryArr[index]} 次重试`)
retryArr[index]++
this.tempThreads++
list.push(formInfo)
handler()
})
// }
})
.catch(e => {
this.$message.error("上传失败,请重试")
})
}
if (finished >= total) {
resolve(currentFileInfo.md5)
}
}
// 控制并发
for (let i = 0
handler()
}
})
},
async mergeRequest(fileMd5) {
return new Promise((resolve, reject) => {
const mergeFormData = new FormData()
mergeFormData.append("fileMd5", fileMd5)
const http = this.isCosType
? getMergeCosFile(mergeFormData)
: getMergeFile(mergeFormData)
http
.then(res => {
if (res.code === 0) {
this.fileList.forEach(item => {
if (item.md5 === fileMd5) {
item.status = "done"
item.fileId = res.data.fileId
this.$emit("fileId", item.fileId)
this.$message.success(item.name + " 文件上传成功!")
// console.log(item.name + ' 文件上传成功!')
resolve("sucess")
}
})
} else {
this.$message.error(res.msg)
}
})
.catch(err => {
// console.log('mergeRequest -> err', err)
reject("合并失败", err)
})
})
},
getFileMD5(file) {
return md5(file.name + file.size + file.lastModifiedDate)
// return md5(file.name + file.size + file.lastModifiedDate + Math.random(1000) + new Date().getTime())
// const reader = new FileReader()
// reader.onload = function (e) {
// file.fileMD5 = md5(e.target.result)
// console.log(e.target.result)
// }
// reader.readAsText(file)
},
handleChange(info) {
this.$set(this, "fileList", info.fileList)
const status = info.file.status
if (status === "done") {
this.$message.success("上传成功")
} else if (status === "uploading") {
// this.$message.info(`${info.file.name} 上传中...`)
} else if (status === "error") {
this.$message.error(`${info.file.name} 上传失败`)
}
},
handleImageRemove(res) {
this.fileList.forEach((item, idx) => {
if (item.uid === res.uid) {
this.fileList.splice(idx, 1)
this.$message.success("删除成功")
}
})
this.$emit("getFileList", this.fileList)
}
}
}
</script>
<style>
/* 隐藏预览功能 */
span.ant-upload-list-item-actions a {
display: none
}
</style>