这篇将介绍如何写一个简单的基于Vue+Element的文件上传控件。
先看效果
控件将具有
1. 上传队列的列表,显示文件名称,大小等信息,可以显示上传进度实时刷新
2. 取消上传
使用Element的uploader控件,上传文件的行为和样式不用自己全部实现,使代码简化。且有足够的扩展性,文件传输请求的代码可以基于axios完全自己重写。我们只用关心核心代码。
先分享一款实用前端面试题库小程序 手机刷题收藏以后用的上 微信搜索 MST题宝库
搭建项目框架
首先建立一个空白的项目,引入Element控件库,具体的操作和使用Element控件库请看官方文档:
后端项目框架的搭建,请阅读:[.Net 6]写一个简单的文件上传控件后端 - 林晓lx - 博客园 (cnblogs.com)
编写文件上传代码
编写文件上传的帮助类,新建ajaxRequire.ts并键入以下内容:
import axios, { CancelTokenSource } from 'axios' //发送网络请求 export const request = async (url: string, methods, data: any, onProgress?: (e)=>void, cancelToken?: CancelTokenSource) => { let token = null let timeout = 3000; if (cancelToken) { token = cancelToken.token timeout = 0; } const service = axios.create() const re = await service.request({ headers: {'Content-Type': 'multipart/form-data'}, url: url, method: methods, data: data, cancelToken: token, timeout: timeout, onUploadProgress: function (progressEvent) { //原生获取上传进度的事件 if (progressEvent.lengthComputable) { if (onProgress) { onProgress(progressEvent); } } }, }) return re as any; } ///获得取消令牌 export const getCancelToken = () => { const source = axios.CancelToken.source(); return source; }
onUploadProgress回调函数将在数据传输进度变化的时候触发,携带progressEvent 原生获取上传进度事件参数,progressEvent.lengthComputable用于判断是否可以进行进度计算
axios.CancelToken.source()可以获得一个源,这个源包含一个唯一Id用于标识哪个请求,和一个cancel函数用于取消请求
编写控件
在App.vue中添加核心的控件 <el-upload>
接着添加属性,注意我们将用自己的方法upload替换el-upload中的上传操作,因此设置action="/",
:http-request="upload",如下:
<el-upload ref="upload" :limit="10" multiple action="/" :http-request="upload"> </el-upload>
在script中添加上传Dto:一些业务相关的数据在这里定义 比如ownerUserId, fileContainerName等,这些数据可以通过表单与文件数据一并上传
export class CreateFileDto { id: string; fileContainerName: string; //文件夹名称 parentId: string; //文件的父Id ownerUserId: number; //文件的归属用户Id fileName: string; mimeType: string; fileType: number; //文件类型 0:文件夹,1:普通文件 file: any; //文件数据 }
method中添加一些帮助类函数:
methods: { successMessage(value = "执行成功") { this.$notify({ title: "成功", message: value, type: "success", }); }, errorMessage(value = "执行错误") { this.$notify.error({ title: "错误", message: value, }); }, FriendlyFileSize(bytes) { bytes = parseFloat(bytes); if (bytes === 0) return "0B"; let k = 1024, sizes = ["B", "KB", "MB", "GB", "TB"], i = Math.floor(Math.log(bytes) / Math.log(k)); return (bytes / Math.pow(k, i)).toPrecision(3) + sizes[i]; }, }
编写提交前置函数,这里将做验证和生成cancelToken:
beforeUpload(file) { var token = getCancelToken(); file.cancelToken = token; let isLt2M = true; if (this.fileSizeLimit < 0) { return true; } isLt2M = file.size / 1024 / 1024 < this.fileSizeLimit; if (!isLt2M) { this.loading = false; this.errorMessage(`"上传文件大小不能超过 ${this.fileSizeLimit}}MB!"`); } return isLt2M; }
编写upload函数,用于组装请求数据并交给 ajaxRequire 执行上传任务
async upload(option) { this.loaded = true; var model = new CreateFileDto(); var file = option.file; model.fileName = file.name; model.fileType = 2; model.mimeType = file.type; model.ownerUserId = 1; model.fileContainerName = "Container1"; model.file = file; var fd = new FormData(); Enumerable.from(model).forEach((c) => { fd.append(c.key, c.value); }); var token = file.cancelToken; await request( this.uploadUrl, "post", fd, (e) => { if (e.total > 0) { e.percent = (e.loaded / e.total) * 100; } option.onProgress(e); }, token ); },
将token将作为取消传输的入口交给ajaxRequire ,自己也保留这个对象用于发送取消命令,相当于“一式两份”。
添加el-upload各阶段函数的订阅
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-remove="handleRemove"
:on-error="handleError"
handleSuccess(response, file, fileList) { this.successMessage("上传成功"); this.loading = false; }, handleError(e, file, fileList) { this.errorMessage(e); this.loading = false; }, handleRemove(file, fileList) { if (file.raw.cancelToken) { file.raw.cancelToken.cancel(); } },
编写上传队列的Html代码:
<el-button ref="uploadButton">上传</el-button> <span slot="file" slot-scope="{ file }"> <div class="filelist-item"> <el-row> <el-col :span="6" class="file-icon-frame"> <i class="el-icon-document file-icon"></i> </el-col> <el-col :span="18"> <el-row> <el-col :span="20"> <label class="file-title"> {{ file.name }} </label> </el-col> <el-col :span="4" style="text-align: right"> <el-button type="danger" icon="el-icon-minus" size="mini" circle @click="handleRemove(file)" ></el-button> </el-col> <el-col :span="24"> <label class="file-size"> {{ FriendlyFileSize(file.size) }} </label> </el-col> <el-col :span="24"> <el-progress :text-inside="true" :stroke-width="26" :percentage="parseInt(file.percentage, 10)" :status=" parseInt(file.percentage, 10) == 100 ? 'success' : '' " > </el-progress ></el-col> </el-row> </el-col> </el-row> </div> </span>
运行
进入后端项目的目录(api),运行:
dotnet run
前端项目目录(web),运行
yarn serve
运行效果:
完整代码:
file-uploader-sample/web at master · jevonsflash/file-uploader-sample (github.com)
项目地址:jevonsflash/file-uploader-sample (github.com)