个人在做文件上传功能的时候,踩过不少的坑,特在此记录下,方便后续的查阅,也帮助有需要的个人在开发中,需要做文件的上传,自己踩过不少的坑,于是决定在此记录以下,方便自己后续查看,也方便有需要的朋友查阅。
实现的功能有:文件上传类型限制、文件大小限制、文件总大小限制、文件同名限制、文件下载、文件删除 和 文件预览图。
效果图如下:
1. 文件类型限制
2. 文件大小限制
3. 文件同名限制
4. 文件总大小限制
5. 文件下载和删除
6. 文件预览图就是上述的框体图片,这个是自己引入的图片,自己会放在附件处。有需要的可到 阿里巴巴icon图库官网 进行下载自己喜欢的图片。
7. 点击保存上传文件等信息
下面来说下大致的实现过程,让大家更明确一些。
前提:要手动上传文件,需要将 :auto-upload="false" 加上,指的是不要自动上传。自己书写下 :http-request="handleUplodFile" 方法,覆盖 action 即可。
自己定义了两个变量:
fileList:[] : 是用于展示选择的文件列表的,如上面的一个个的框体。
realUploadFile:[] :这个是真实的要上传给后端的文件,该数组的类型是 二进制的Object 类型
下面直接说下关键的代码
1. 文件上传的时候,触发 on-change 事件,在这可做一些判断。文件类型限制、文件大小限制、文件同名限制和文件总大小限制等等。
// 文件勾选触发
changUpload(file) {
const { uid, name, size } = file
// 文件类型限制
const fileTypeFlag = /^.png|.jpg|.jpeg|.gif|.txt|.doc|.docx|.xls|.xlsx|.pdf|.zip|.7z|.rar$/.test(
name.substring(name.lastIndexOf('.')).toLowerCase()
)
if (!fileTypeFlag) {
this.$message.warning(
'文件类型只能是.png|.jpg|.jpeg|.gif|.doc|.docx|.xls|.xlsx|.pdf|.zip|.7z|.rar|.txt'
)
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
return
}
// 文件大小进行校验
if (size > 1024 * 1024 * 100) {
this.$message.warning('文件大小不能超过100Mb')
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
return
}
// 文件总大小限制
const totalSize = this.modelForm.fileList.reduce(
(total, item) => total + item.size,
0
)
if (totalSize + size > 1024 * 1024 * 100) {
this.$message.warning('总文件大小不能超过100Mb')
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
return
}
// 文件重名限制
const findCommonNameIndex = this.modelForm.fileList.findIndex(
item => item.name == name
)
if (findCommonNameIndex !== -1) {
this.$message.warning('不能上传同名的文件')
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
return
}
// 将上传的文件信息存入列表
this.modelForm.fileList.push(file)
this.realUploadFile = []
},
2. 文件的删除和下载。删除其实就是将已经勾选的文件移除列表,下载就是创建 a 标签,通过href 的方式下载。注意:在这不推荐使用 window.open, 因为下载下来的 的文件名是 哈希名,不符合要求。
// 移除不需要的文件
handleRemove(file) {
const { uid } = file
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
},
// 下载文件
handleDownload(file) {
const { url, name } = file
let downloadLink = document.createElement('a')
downloadLink.href = url
downloadLink.download = name
document.body.appendChild(downloadLink)
downloadLink.click()
document.body.removeChild(downloadLink)
},
3. 实际要上传的文件。:http-request="handleUplodFile" 该事件就是自己实际要上传的文件,触发该方法是需要通过 this.$refs['uploadBlock'].submit() 来进行,然后就会将需要上传的文件压入数组中。
// 要上传文件压入数组
handleUplodFile(file) {
this.realUploadFile.push(file.file)
},
// 点击保存
handleHttpUpload() {
this.$refs['uploadBlock'].submit()
......
}
4. 点击保存,上传文件等相关信息。这里使用到的是 FormData() 对象,通过 append 追加的方式,将文件和其他信息放入该 FromData() 对象中。
// 点击保存
handleHttpUpload() {
this.$refs['uploadBlock'].submit()
let formData = new FormData() //formdata格式
formData.append('info', JSON.stringify(this.modelForm))
this.realUploadFile.forEach(file => {
formData.append('file', file, file.name)
})
fileUpload(formData).then(resonse => {
console.log(resonse)
})
// 一定要先清空真实文件数组,免得点击几次,真实文件一直压入该数组
this.$nextTick(() => {
this.realUploadFile = []
})
},
5. 自己写的后端 api 接口,以及如何使用她
/***** api 接口******/
export function fileUpload(data) {
return request({
url: '/file/upload',
method: 'post',
data
})
}
/*****调用api*****/
import { fileUpload } from '@/api/user'
......
fileUpload(formData).then(resonse => {
console.log(resonse)
})
/******** 提示:若是需要查看到文件上传的进度,可以使用如下配置 ******************/
/***** api 接口******/
export function fileUpload(data, config) {
return request({
url: '/file/upload',
method: 'post',
data,
...config
})
}
/*****调用api*****/
import { fileUpload } from '@/api/user'
......
const config = {
timeout: 120000,
onUploadProgress: function(process) {
const num = process.loaded / process.total * 100 | 0
// 将数据赋值给进度条
this.processNum = num
}
}
fileUpload(formData, config).then(resonse => {
console.log(resonse)
})
6. 完整的demo 代码如下
<template>
<div>
<el-form ref="searchform" :model="modelForm" label-width="150px">
<el-form-item label="select多选demo:" prop="selectValue">
<el-select
v-model="modelForm.selectValue"
@change="changeSelect"
multiple
clearable
placeholder="请选择"
>
<el-option key="selectAll" label="全部" value="selectAll"></el-option>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="附件" prop="fileList">
<el-upload
ref="uploadBlock"
action="#"
list-type="picture-card"
:file-list="modelForm.fileList"
accept=".png,.jpg,.jpeg,.gif,.txt,.doc,.docx,.xls,.xlsx,.pdf,.zip,.7z,.rar"
:auto-upload="false"
:on-change="changUpload"
:http-request="handleUplodFile"
>
<i slot="default" class="el-icon-plus"></i>
<div slot="file" slot-scope="{ file }">
<img
class="el-upload-list__item-thumbnail"
:src="getPreviwImg(file.name)"
alt=""
/>
<el-tooltip :content="file.name" placement="bottom" effect="light">
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-delete"
@click="handleDownload(file)"
>
<i class="el-icon-download"></i>
</span>
<span
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete"></i>
</span>
</span>
</el-tooltip>
</div>
</el-upload>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleHttpUpload">保存</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { fileUpload } from '@/api/user'
export default {
data() {
return {
options: [
{
value: '选项1',
label: '黄金糕'
},
{
value: '选项2',
label: '双皮奶'
},
{
value: '选项3',
label: '蚵仔煎'
},
{
value: '选项4',
label: '龙须面'
},
{
value: '选项5',
label: '北京烤鸭'
}
],
modelForm: {
selectValue: [],
fileList: []
},
realUploadFile: [], // 真实上传的文件
selectAll: false // 用于标识是否全选--默认不全选
}
},
created() {},
methods: {
changeSelect(value) {
// selectAll 为true 的时候,就走全选分支,全选后出现的情况就是取消权限
if (this.selectAll) {
this.selectAll = false
if (value.indexOf('selectAll') > -1) {
this.modelForm.selectValue = value.filter(p => p != 'selectAll')
} else {
this.modelForm.selectValue = []
}
} else {
// 是否点击了‘全选’选项
if (value.indexOf('selectAll') > -1) {
// 有‘全选’选项,则将‘全部’和其他值放置一块
const optionsValue = []
this.options.forEach(item => {
optionsValue.push(item.value)
})
this.modelForm.selectValue = ['selectAll', ...optionsValue]
this.selectAll = true
} else {
// 若是勾选选择的长度和提供的选项长度是一样的,则是 ‘全选’
if (value.length === this.options.length) {
const optionsValue = []
this.options.forEach(item => {
optionsValue.push(item.value)
})
this.modelForm.selectValue = ['selectAll', ...optionsValue]
this.selectAll = true
} else {
// 都是单选
this.modelForm.selectValue = value
}
}
}
// 真实的勾选值
const realSelect = this.modelForm.selectValue.filter(
item => item != 'selectAll'
)
console.log('realSelect', realSelect)
},
// 移除不需要的文件
handleRemove(file) {
const { uid } = file
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
},
// 下载文件
handleDownload(file) {
const { url, name } = file
let downloadLink = document.createElement('a')
downloadLink.href = url
downloadLink.download = name
document.body.appendChild(downloadLink)
downloadLink.click()
document.body.removeChild(downloadLink)
},
// 文件勾选触发
changUpload(file) {
const { uid, name, size } = file
// 文件类型限制
const fileTypeFlag = /^.png|.jpg|.jpeg|.gif|.txt|.doc|.docx|.xls|.xlsx|.pdf|.zip|.7z|.rar$/.test(
name.substring(name.lastIndexOf('.')).toLowerCase()
)
if (!fileTypeFlag) {
this.$message.warning(
'文件类型只能是.png|.jpg|.jpeg|.gif|.doc|.docx|.xls|.xlsx|.pdf|.zip|.7z|.rar|.txt'
)
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
return
}
// 文件大小进行校验
if (size > 1024 * 1024 * 100) {
this.$message.warning('文件大小不能超过100Mb')
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
return
}
// 文件总大小限制
const totalSize = this.modelForm.fileList.reduce(
(total, item) => total + item.size,
0
)
if (totalSize + size > 1024 * 1024 * 100) {
this.$message.warning('总文件大小不能超过100Mb')
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
return
}
// 文件重名限制
const findCommonNameIndex = this.modelForm.fileList.findIndex(
item => item.name == name
)
if (findCommonNameIndex !== -1) {
this.$message.warning('不能上传同名的文件')
const selectFileList = this.modelForm.fileList.filter(item => {
return item.uid != uid
})
this.modelForm.fileList = selectFileList
return
}
// 将上传的文件信息存入列表
this.modelForm.fileList.push(file)
this.realUploadFile = []
},
// 要上传文件压入数组
handleUplodFile(file) {
this.realUploadFile.push(file.file)
},
// 点击保存
handleHttpUpload() {
this.$refs['uploadBlock'].submit()
let formData = new FormData() //formdata格式
formData.append('info', JSON.stringify(this.modelForm))
this.realUploadFile.forEach(file => {
formData.append('file', file, file.name)
})
fileUpload(formData).then(resonse => {
console.log(resonse)
})
this.$nextTick(() => {
this.realUploadFile = []
})
},
getImgUrl(image) {
return require(`@/assets/images/file/${image}`)
},
getPreviwImg(filename) {
filename = filename
.substring(filename.lastIndexOf('.'))
.toLowerCase()
.split('.')[1]
const imageObject = {
png: 'icon-png.png',
jpg: 'icon-jpg.png',
jpeg: 'icon-jpeg.png',
gif: 'icon-gif.png',
doc: 'icon-doc.png',
docx: 'icon-doc.png',
xls: 'icon-xls.png',
xlsx: 'icon-xls.png',
pdf: 'icon-pdf.png',
zip: 'icon-zip.png',
'7z': 'icon-zip.png',
rar: 'icon-zip.png',
txt: 'icon-txt.png'
}
return this.getImgUrl(imageObject[filename])
}
}
}
</script>
<style lang="scss" scoped></style>
上述用到图片的附件: