文件上传
上传方法:
- action
:http-request="onUpload"
自定义上传方法
上传方式
- 手动上传
调用this.$refs.uploadFile.submit()
- 自动上传
:auto-upload=“true”
上传前校验:beforeUpload
返回boolean 与 promise结果
如果false,reject 则停止上传
axios 上传进度
onUploadProgress 钩子 获取进度
设置fileList 中file的status与 percentage 看element 源码
status 状态:ready | uploading | sucess | fail
检验与上传都是单个进行的
中断上传, 取消上传
axios的cancelToken
<el-upload
ref="uploadFile"
action=""
:on-change="onChange"
:http-request="onUpload"
:on-remove="onRemove"
:before-upload="beforeUpload"
:file-list="fileList"
:auto-upload="true"
:limit="1"
:on-exceed="handleExceed"
class="document-upload"
>
<el-button :loading="loading" size="small" type="primary" class="mr">点击上传</el-button>
<div slot="tip" class="el-upload__tip">仅支持文件大小不超过50Mb</div>
</el-upload>
</div>
import SparkMD5 from 'spark-md5'
import { upload, CancelToken } from '@/utils/docRequset'
import { validateFileMd5 } from '@/api/file'
data() {
return {
TipText: '当前仅支持不大于50M的文件',
typeTip: '文档格式支持:.doc、.docx、.xls 、.xlt、 .xlsx、.xlsm、.ppt、.pptx、.pdf、.pages',
MineTypes: ['application/msword',
'application/wps-writer', // doc
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/x-xls',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel.sheet.macroEnabled.12', // windows
'application/vnd.ms-excel.sheet.macroenabled.12', // mac
'application/vnd.ms-powerpoint',
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/pdf',
''],
extensions: ['doc', 'docx', 'xls', 'xlt', 'xlsx', 'xlsm', 'ppt', 'pptx', 'pdf', 'pages']
}
}
async getFileMd5(file) {
const blobSlice = window.File.prototype.slice || window.File.prototype.mozSlice || window.File.prototype.webkitSlice
const chunkSize = 2097152 // Read in chunks of 2MB
const chunks = Math.ceil(file.size / chunkSize)
let currentChunk = 0
const spark = new SparkMD5.ArrayBuffer()
const fileReader = new FileReader()
const loadNext = () => {
const start = currentChunk * chunkSize
const end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize
fileReader.readAsArrayBuffer(blobSlice.call(file, start, end))
}
return new Promise((resolve, reject) => {
fileReader.onload = (e) => {
spark.append(e.target.result) // Append array buffer
currentChunk++
if (currentChunk < chunks) {
loadNext()
} else {
console.log('finished loading')
const res = spark.end()
console.info('computed hash', res) // Compute hash
resolve(res)
}
}
fileReader.onerror = () => {
reject()
console.warn('oops, something went wrong.')
}
loadNext()
})
},
// 上传前校验 返回false/reject() 不会执行onUpload
beforeUpload(file) {
return new Promise((resolve, reject) => {
// console.log(file)
const acceptType = this.MineTypes.indexOf(file.type) > -1
if (!acceptType) {
this.$message.error('不支持上传此类型文件')
return reject()
}
const acceptExt = this.extensions.indexOf(getExt(file.name)) > -1
if (!acceptExt) {
this.$message.error('不支持上传此类型文件')
return reject()
}
const passSize = file.size / 1024 / 1024 < 50
if (!passSize) {
this.$message.error(this.TipText)
return reject()
}
return this.getFileMd5(file).then(fileMd5 => {
file.fileMd5 = fileMd5
if (this.form.channel) file.channel = this.form.channel
return validateFileMd5({ fileMd5 })
}).then(res => {
if (res.count > 0) {
return this.$confirm('该文件已存在, 是否继续上传?', '提示', {
confirmButtonText: '继续',
cancelButtonText: '取消',
type: 'warning'
})
} else return true
}).then(res => {
res ? resolve() : reject()
}).catch(e => {
reject()
})
})
},
async onUpload(req) {
// 上传
const rawFile = {
status: 'ready',
name: req.file.name,
size: req.file.size,
percentage: 0,
uid: req.file.uid,
file: req.file,
source: CancelToken.source()
}
this.fileList.push(rawFile)
rawFile.status = 'uploading'
rawFile.percentage = 0
upload(rawFile.file, (progressEvent) => {
// Do whatever you want with the native progress event
rawFile.status = 'uploading'
rawFile.percentage = Math.floor(progressEvent.loaded / progressEvent.total * 100) || 0
console.log('上传中:', rawFile.percentage)
}, rawFile.source.token).then(res => {
if (res.data) {
rawFile.reponseData = res.data
this.changeValue()
rawFile.status = 'success'
} else {
throw Error('res.data')
}
}).catch((error) => {
rawFile.status = 'ready'
rawFile.percentage = 0
const index = this.fileList.findIndex(v => v.file.uid === rawFile.file.uid)
if (index > -1) this.fileList.splice(index, 1)
// this.$refs.uploadFile.clearFiles()
this.$message.error('图片上传失败')
console.log(error)
}).finally(() => {
this.loading = false
})
},
onRemove(file, fileList) {
if (file.status === 'uploading') {
file.source.cancel('中止上传')
file.source = CancelToken.source()
}
// elementui bug 移除时候fileList得手动移除
const index = this.fileList.findIndex(v => v.uid === file.uid)
if (index > -1) this.fileList.splice(index, 1)
this.changeValue()
},
submit() {
this.$refs.uploadFile.submit()
},
handleExceed(files, fileList) {
this.$message({
type: 'warning',
message: '已有文件,请先删除当前已有文件',
showClose: true
})
}
// 中断上传
export const CancelToken = axios.CancelToken // axios的config中提供了一个cancelToken属性,可以通过传递一个新的CancelToken对象来在请求的任何阶段关闭请求。
// export const source = CancelToken.source()
export const upload = (file, onUploadProgress, cancel) => {
const params = new FormData()
params.append('file', file) // 字段名称
params.append('fileMd5', file.fileMd5)
return axios.post(`/?action=uploads`, params, {
baseURL,
withCredentials: true, // send cookies when cross-domain requests
timeout: 300000, // request timeout
headers: {
'Content-Type': 'multipart/form-data', // 类型
'X-Token': getToken()
},
onUploadProgress,
cancelToken: cancel
})
}