一文搞懂element-ui的文件上传
- 文件上传逻辑
携带token
上传文件到文件服务器,上传成功服务器返回体里包含fileId
和fileName
,上传失败返回失败code
, - 文件下载逻辑
根据fileId
调接口获取二进制流文件,根据fileName
的后缀获取文件类型,把二进制文件根据文件类型生成文件。 其他逻辑
- 文件上传前需判断上传文件类型(文件后缀)及文件大小是否超限,简单不做说明
碰到问题如下
问题 1.文件上传需要携带 token,并放置于 header 里
解决方案;
使用el-upload
提供的header
即可
<el-upload :headers="headers" />
data(){
return {
headers:{token:"111"}
}
}
问题 2.图片预览时如果图片不存在可能会返回二进制的 json 文件流,需要特殊处理
判断预览时返回的二进制流,分情况处理
handlePreview(file) {
// console.log('要预览的文件是:', file)
// 获取服务端返回的文件id和文件名
const fileId = file.response.body.fileId
const fileName = file.response.body.fileName
axios.post(
this.DOWNLOAD_URL,
{
body: { fileId: fileId }
},
{
// 设置axios的headers和返回数据类型
headers: this.headers,
responseType: 'blob'
}
)
.then(res => {
// 如果文件未找到还是可能会返回二进制格式的json信息,需要进行处理
// 正常情况下会返回需要的二进制文件
if (this.handleMessage(res)) {
// 处理正常情况的二进制文件
var fileType = this.getFileType(fileName)
if (fileType === 'image') {
this.downImage(res.data, fileName)
} else {
console.info('非图片格式暂无法预览')
}
}
})
},
handleMessage(res) {
// 未处理过的response对象,如果为正常的二进制流文件返回true,如果为二进制的json文件则返回false并展示json内容
if (res.data && res.data.type) {
if (res.data.type === 'application/octet-stream') {
// 正常的二进制流文件
return true
} else if (res.data.type === 'application/json') {
// 异常的二进制的JSON文件
const reader = new FileReader()
reader.readAsText(res.data, 'utf-8')
reader.onload = e => {
const result = JSON.parse(reader.result)
const message = '下载资源文件失败' + reader.result
console.error('下载资源文件失败:', result)
this.$message({
message: message,
type: 'error',
duration: 5 * 1000
})
}
return false
}
} else {
console.error(
'函数入参response对象应该是一个完整的对象,应该包含data.type属性'
)
}
},
getFileType(fileName) {
// 更多文件类型 参考 https://www.cnblogs.com/zhongcj/archive/2008/11/03/1325293.html
const arr = fileName.split('.')
const len = arr.length
let str = ''
if (len > 1) {
const allowedImageType = this.allowedImageType ||['jpg','jpeg','png']
const allowedVideoType = this.allowedVideoType || ['mp4']
const fileType = arr[arr.length - 1].toLocaleLowerCase()
if (allowedImageType.includes(fileType)) {
str = 'image'
} else if (allowedVideoType.includes(fileType)) {
str = 'video'
}
}
return str
},
downImage(blobData, fileName) {
const blob = new Blob([blobData], { type: 'image/jpeg' })
const reader = new FileReader()
reader.readAsDataURL(blob)
reader.onload = e => {
const url = URL.createObjectURL(blob)
// 取到url,直接在页面展示即可
}
},
问题 3.el-upload 组件展示为上传成功实际上传失败问题
问题产生原因: element-ui
里upload
组件是根据httpCode
来判断的,如果为httpCode<200||httpCode>=300
则判断为上传失败,其他情况则标记为上传成功。
而开发过程却是不处理httpCode
而是根据responseBody
里的code
字段进行判断,虽然我也感觉通过httpCode
更合适,但是碰到很多情况都是通过响应体里的code
来判断的(难不成是因为后端同事不知道怎么处理httpCode
?)
handleSuccess(res, file, fileList) {
// 上传成功函数的res是responseBody的body体
// 处理element-ui认为上传成功(他是通过httpCode为200判定为成功的),实际上传失败情况(通过responseBody判断)
if (res.code && res.code !== 0) {
// 上传不成功给出提示信息
this.$message({
message: res.message || 'error',
type: 'error',
duration: 5 * 1000
})
this.$nextTick(function() {
// 移除上传失败的文件,code为0即为成功
const successFileList = fileList.filter(ele => {
return ele.response.code + '' === '0'
})
// 重新设置列表为正确的列表
// this.fileList = successFileList
// 假上传成功时也需要进行一次处理
this.$emit('my-update', successFileList)
})
}
},
问题 4.如何在包装 el-upload 的自定义组件里获取到最新的已上传的文件列表
使用自定义的v-model
解决在组件最外层获取已上传的文件列表
// 自定义组件内,设置v-model
export default {
model: {
prop: 'fileList',
event: 'my-update',
},
props: {
// element-ui上传组件的文件列表数组
fileList: {
type: Array,
default() {
return []
},
},
},
methods: {
// 监听ele-upload的change事件 根据文档仅添加文件、上传成功和上传失败时都会被调用
handleChange(file, fileList) {
// console.log('文件发生变化', file, fileList)
this.$emit('my-update', fileList)
},
// 删除文件后需要同步触发下事件
handleRemove(file, fileList) {
const fileId = file.response.body.fileId
// console.log('执行删除事件,要删除的文件id', fileId, fileList)
const selectedFileList = fileList.filter(ele => {
return fileId !== ele.response.body.fileId
})
// console.log('删除后文件列表:', selectedFileList)
this.$emit('my-update', selectedFileList)
},
// 同时假上传成功时也需要进行一次处理
},
}
<!-- 父组件里使用 -->
<upload-box v-model="uploadFileList" />
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。