前言
项目上传文件之前是阿里云直传的方式,因上传一个文件就要1GB,所以研究了一下分片上传模式!阿里云上单个大文件分片上传的API文档写的非常清楚,在此给大家安排一下多文件大文件上传,以下引用阿里云的一段话!
当需要上传的文件较大时,可以通过MultipartUpload接口进行分片上传。分片上传是指将要上传的文件分成多个数据块(Part)来分别上传。当其中一些分片上传失败后,OSS将保留上传进度记录,再次重传时只需要上传失败的分片,而不需要重新上传整个文件。一般大于100 MB的文件,建议采用分片上传的方法,通过断点续传和重试,提高上传成功率。
1、oss-sectioning.js
文件配置
import Vue from 'vue
function infoClient () {
let OSS = require('ali-oss')
let ossConfig = {
region: 'xxx',
// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。
// 强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录RAM控制台创建RAM账号。
accessKeyId: 'xxx',
accessKeySecret: 'xxx',
bucket: 'xxx'
}
return new OSS(ossConfig)
}
let tempCheckpointObj = {}
上传文件、断点恢复上传、暂停取消上传
export default class OssSectionUpload {
constructor (fileName, file, url) {
this.fileName = fileName
this.file = file
this.url = url
this.client = infoClient()
}
// 上传文件
async multipartUpload (callback) {
try {
// object-key可以自定义为文件名(例如file.txt)或目录(例如abc/test/file.txt)的形式,实现将文件上传至当前Bucket或Bucket下的指定目录。
let that = this
await this.client.multipartUpload(this.url, this.file, {
parallel: 4,
partSize: 1000 * 1024,
progress: function (p, checkpoint) {
// 断点记录点。浏览器重启后无法直接继续上传,您需要手动触发上传操作。
tempCheckpointObj[that.fileName] = checkpoint
callback(p, that.fileName)
},
meta: { year: (new Date()).getFullYear(), people: Vue.prototype.$auth.user().userName }
}).then(res => {
Vue.prototype.$message.success('上传成功')
}).catch(() => {
if (this.client.isCancel()) {
Vue.prototype.$message.error('上传失败')
} else {
Vue.prototype.$message.error('网络异常')
}
})
} catch (e) {
console.log(e)
} }
// 恢复上传
async resumeUpload (callback) {
try {
let that = this
await this.client.multipartUpload(this.fileName, this.file, {
parallel: 4,
partSize: 1000 * 1024,
progress: function (p, checkpoint) {
this.tempCheckpoint = checkpoint
callback(p)
},
checkpoint: tempCheckpointObj[that.fileName],
meta: { year: (new Date()).getFullYear(), people: Vue.prototype.$auth.user().userName }
}).then(res => {
Vue.prototype.$message.success('上传成功')
}).catch(() => {
if (this.client.isCancel()) {
Vue.prototype.$message.error('上传失败')
} else {
Vue.prototype.$message.error('网络异常')
}
})
} catch (e) {
console.log(e)
}
}
// 取消上传
cancleUpload () {
this.client.cancel()
}}
2、vue + element-ui
<template>
<div :class="$style.upload">
<el-upload
ref='upload'
action='aaa'
:show-file-list="true"
:file-list="playUrls"
:http-request="uploadSectionFile"
:on-remove="onRemove"
:multiple="multiple"
:on-preview="handlePreview">
<el-button size="small" type="primary" >{{btnText}}</el-button>
</el-upload>
</div>
</template>
<script>
import OssSectionUpload from '@/utils/oss-sectioning'
export default {
name: 'pt-upload-big-file',
props: {
objectId: {
type: String,
default: ''
},
value: {
type: [Array, Object, String],
default: function () {
return []
}
},
resourceConfig: {
type: String,
default: ''
},
isPublic: {
type: Boolean,
default: false
},
multiple: {
type: Boolean,
default: false
},
btnText: {
type: String,
default: '点击上传'
}
},
data () {
return {
progress: 0,
playUrls: [],
currentValue: this.getValue(),
isUpload: false
}
},
computed: {
resourceUrl () {
return this.isPublic ? this.$publicResourcesUrl : this.$privateResourcesUrl
}
},
watch: {
value: {
handler: function (val) {
if (val && JSON.stringify(val) !== '{}') {
this.currentValue = val
}
},
immediate: true
},
isUpload (val) {
this.$emit('isUpload', val)
}
},
methods: {
handlePreview (file) {
if (this.playUrls.every(item => item.percentage === 100)) {
this.$message.info('已全部上传完毕')
return
}
this.playUrls.forEach((ele, idx) => {
if (ele.name === file.name) {
if (ele.continueUpload) {
this.$message.info('恢复上传')
ele.client.resumeUpload((p) => {
this.playUrls[idx].percentage = parseFloat((p * 100))
if (this.playUrls.every(item => item.percentage === 100)) {
this.isUpload = true
}
})
} else {
this.$message.info('暂停上传')
ele.client.cancleUpload()
}
ele.continueUpload = !ele.continueUpload
}
})
},
getValue () {
return this.multiple ? [] : {}
},
dealUploadData (params, url) {
let func = new OssSectionUpload(changeFileName, params.file, url)
this.playUrls.push({
url: this.resourceConfig + '/' + this.objectId + '/' + params.file.name,
name: params.file.name, client: func, status: 'uploading',
percentage: 0,
continueUpload: false
})
func.multipartUpload((p, fileName) => {
this.playUrls.forEach((ele, idx) => {
if (fileName === ele.name) {
this.playUrls[idx].percentage = parseFloat((p * 100))
}
})
if (this.playUrls.every(item => item.percentage === 100)) {
this.isUpload = true
}
})
},
async uploadSectionFile (params) {
let url = this.resourceConfig + '/' + this.objectId + '/' + params.file.name
this.dealUploadData(params, url)
let data = {
url,
name: params.file.name
}
if (this.multiple) {
this.currentValue.push(data)
} else {
this.currentValue = data
}
this.$emit('input', this.currentValue)
},
clearFiles () {
this.$refs.upload.clearFiles()
},
onRemove (file, fileList) {
if (this.multiple) {
for (let i = 0; i < this.playUrls.length; i++) {
if (this.playUrls[i].name === file.name) {
this.playUrls[i].client.cancleUpload()
this.currentValue.splice(i, 1)
this.playUrls.splice(i, 1)
break
}
}
this.$emit('input', this.currentValue)
} else {
this.$emit('input', '')
this.playUrls[0].client.cancleUpload()
this.playUrls = []
}
}
}}
</script>
<style lang="less" module>
.upload{
:global {
:focus{
outline:none;
}
.el-upload{
width: 60%;
text-align: left;
}
}}
</style>
总结
希望对正在看要完成这个需求的你有所帮助,也请各位大佬有建议多多提出来,感谢