问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501
你好,欢迎来到懂视!登录注册
当前位置: 首页 - 正文

vue文件上传插件有哪些?

发布网友 发布时间:2022-04-26 06:07

我来回答

2个回答

懂视网 时间:2022-05-15 00:41

这次给大家带来vue封装轻量级文件上传组件,vue封装轻量级文件上传组件的注意事项有哪些,下面就是实战案例,一起来看一下。

一、之前遇到的一些问题

项目中多出有上传文件的需求,使用现有的UI框架实现的过程中,不知道什么原因,总会有一些莫名其妙的bug。比如用某上传组件,明明注明(:multiple="false"),可实际上还是能多选,上传的时候依然发送了多个文件;又比如只要加上了(:file-list="fileList")属性,希望能手动控制上传列表的时候,上传事件this.refs.[upload(组件ref)].submit()就不起作用了,传不了。总之,懒得再看它怎么实现了,我用的是功能,界面本身还是要重写的,如果坚持用也会使项目多很多不必要的逻辑、样式代码……

之前用Vue做项目用的视图框架有element-ui,团队内部作为补充的zp-ui,以及iview。框架是好用,但是针对自己的项目往往不能全部拿来用,尤其是我们的设计妹子出的界面与现有框架差异很大,改源码效率低又容易导致未知的bug,于是自己就抽时间封装了这个上传组件。

二、代码与介绍

父组件

<template>
 <p class="content">
 <label for="my-upload">
 <span>上传</span>
 </label>
 <my-upload
 ref="myUpload"
 :file-list="fileList"
 action="/uploadPicture"
 :data="param"
 :on-change="onChange"
 :on-progress="uploadProgress"
 :on-success="uploadSuccess"
 :on-failed="uploadFailed"
 multiple
 :limit="5"
 :on-finished="onFinished">
 </my-upload>
 <button @click="upload" class="btn btn-xs btn-primary">Upload</button>
 </p>
</template>
<script>
import myUpload from './components/my-upload'
export default {
 name: 'test',
 data(){
 return {
 fileList: [],//上传文件列表,无论单选还是支持多选,文件都以列表格式保存
 param: {param1: '', param2: '' },//携带参数列表
 }
 },
 methods: {
 onChange(fileList){//监听文件变化,增减文件时都会被子组件调用
 this.fileList = [...fileList];
 },
 uploadSuccess(index, response){//某个文件上传成功都会执行该方法,index代表列表中第index个文件
 console.log(index, response);
 },
 upload(){//触发子组件的上传方法
 this.$refs.myUpload.submit();
 },
 removeFile(index){//移除某文件
 this.$refs.myUpload.remove(index);
 },
 uploadProgress(index, progress){//上传进度,上传时会不断被触发,需要进度指示时会很有用
 const{ percent } = progress;
 console.log(index, percent);
 },
 uploadFailed(index, err){//某文件上传失败会执行,index代表列表中第index个文件
 console.log(index, err);
 },
 onFinished(result){//所有文件上传完毕后(无论成败)执行,result: { success: 成功数目, failed: 失败数目 }
 console.log(result);
 }
 },
 components: {
 myUpload
 }
}
</script>

父组件处理与业务有关的逻辑,我特意加入索引参数,便于界面展示上传结果的时候能够直接操作第几个值,并不是所有方法都必须的,视需求使用。

子组件

<template>
<p>
 <input style="display:none" @change="addFile" :multiple="multiple" type="file" :name="name" id="my-upload"/>
</p>
</template>

上传文件,html部分就这么一对儿标签,不喜欢复杂啰嗦

<script>
export default {
 name: 'my-upload',
 props: {
 name: String,
 action: {
 type: String,
 required: true
 },
 fileList: {
 type: Array,
 default: []
 },
 data: Object,
 multiple: Boolean,
 limit: Number,
 onChange: Function,
 onBefore: Function,
 onProgress: Function,
 onSuccess: Function,
 onFailed: Function,
 onFinished: Function
 },
 methods: {}//下文主要是methods的介绍,此处先省略
}
</script>

这里定义了父组件向子组件需要传递的属性值,注意,这里把方法也当做了属性传递,都是可以的。

自己写的组件,没有像流行框架发布的那样完备和全面,另外针对开头提到的绑定file-list就不能上传了的问题(更可能是我的姿势不对),本人也想极力解决掉自身遇到的这个问题,所以希望能对文件列表有绝对的控制权,除了action,把file-list也作为父组件必须要传递的属性。(属性名父组件使用“-”连接,对应子组件prop中的驼峰命名)

三、主要的上传功能

methods: {
 addFile, remove, submit, checkIfCanUpload
}

methods内一共4个方法,添加文件、移除文件、提交、检测(上传之前的检验),下面一一讲述:

1.添加文件

addFile({target: {files}}){//input标签触发onchange事件时,将文件加入待上传列表
 for(let i = 0, l = files.length; i < l; i++){
 files[i].url = URL.createObjectURL(files[i]);//创建blob地址,不然图片怎么展示?
 files[i].status = 'ready';//开始想给文件一个字段表示上传进行的步骤的,后面好像也没去用......
 }
 let fileList = [...this.fileList];
 if(this.multiple){//多选时,文件全部压如列表末尾
 fileList = [...fileList, ...files];
 let l = fileList.length;
 let limit = this.limit;
 if(limit && typeof limit === "number" && Math.ceil(limit) > 0 && l > limit){//有数目限制时,取后面limit个文件
 limit = Math.ceil(limit);
// limit = limit > 10 ? 10 : limit;
 fileList = fileList.slice(l - limit);
 }
 }else{//单选时,只取最后一个文件。注意这里没写成fileList = files;是因为files本身就有多个元素(比如选择文件时一下子框了一堆)时,也只要一个
 fileList = [files[0]];
 }
 this.onChange(fileList);//调用父组件方法,将列表缓存到上一级data中的fileList属性
 },

2.移除文件

这个简单,有时候在父组件叉掉某文件的时候,传一个index即可。

remove(index){
 let fileList = [...this.fileList];
 if(fileList.length){
 fileList.splice(index, 1);
 this.onChange(fileList);
 }
},

3.提交上传

这里使用了两种方式,fetch和原生方式,由于fetch不支持获取上传的进度,如果不需要进度条或者自己模拟进度或者XMLHttpRequest对象不存在的时候,使用fetch请求上传逻辑会更简单一些

submit(){
 if(this.checkIfCanUpload()){
 if(this.onProgress && typeof XMLHttpRequest !== 'undefined')
 this.xhrSubmit();
 else
 this.fetchSubmit();
 }
},

4.基于上传的两套逻辑,这里封装了两个方法xhrSubmit和fetchSubmit

fetchSubmit

fetchSubmit(){
 let keys = Object.keys(this.data), values = Object.values(this.data), action = this.action;
 const promises = this.fileList.map(each => {
 each.status = "uploading";
 let data = new FormData();
 data.append(this.name || 'file', each);
 keys.forEach((one, index) => data.append(one, values[index]));
 return fetch(action, {
 method: 'POST',
 headers: {
 "Content-Type" : "application/x-www-form-urlencoded"
 },
 body: data
 }).then(res => res.text()).then(res => JSON.parse(res));//这里res.text()是根据返回值类型使用的,应该视情况而定
 });
 Promise.all(promises).then(resArray => {//多线程同时开始,如果并发数有限制,可以使用同步的方式一个一个传,这里不再赘述。
 let success = 0, failed = 0;
 resArray.forEach((res, index) => {
 if(res.code == 1){
 success++;  //统计上传成功的个数,由索引可以知道哪些成功了
 this.onSuccess(index, res);
 }else if(res.code == 520){ //约定失败的返回值是520
 failed++;  //统计上传失败的个数,由索引可以知道哪些失败了
 this.onFailed(index, res);
 }
 });
 return { success, failed }; //上传结束,将结果传递到下文
 }).then(this.onFinished); //把上传总结果返回
},

xhrSubmit

xhrSubmit(){
 const _this = this;
 let options = this.fileList.map((rawFile, index) => ({
 file: rawFile,
 data: _this.data,
 filename: _this.name || "file",
 action: _this.action,
 onProgress(e){
 _this.onProgress(index, e);//闭包,将index存住
 },
 onSuccess(res){
 _this.onSuccess(index, res);
 },
 onError(err){
 _this.onFailed(index, err);
 }
 }));
 let l = this.fileList.length;
 let send = async options => {
 for(let i = 0; i < l; i++){
 await _this.sendRequest(options[i]);//这里用了个异步方法,按次序执行this.sendRequest方法,参数为文件列表包装的每个对象,this.sendRequest下面紧接着介绍
 }
 };
 send(options);
},

这里借鉴了element-ui的上传源码

sendRequest(option){
 const _this = this;
 upload(option);
 function getError(action, option, xhr) {
 var msg = void 0;
 if (xhr.response) {
 msg = xhr.status + ' ' + (xhr.response.error || xhr.response);
 } else if (xhr.responseText) {
 msg = xhr.status + ' ' + xhr.responseText;
 } else {
 msg = 'fail to post ' + action + ' ' + xhr.status;
 }
 var err = new Error(msg);
 err.status = xhr.status;
 err.method = 'post';
 err.url = action;
 return err;
 }
 function getBody(xhr) {
 var text = xhr.responseText || xhr.response;
 if (!text) {
 return text;
 }
 try {
 return JSON.parse(text);
 } catch (e) {
 return text;
 }
 }
 function upload(option) {
 if (typeof XMLHttpRequest === 'undefined') {
 return;
 }
 var xhr = new XMLHttpRequest();
 var action = option.action;
 if (xhr.upload) {
 xhr.upload.onprogress = function progress(e) {
 if (e.total > 0) {
 e.percent = e.loaded / e.total * 100;
 }
 option.onProgress(e);
 };
 }
 var formData = new FormData();
 if (option.data) {
 Object.keys(option.data).map(function (key) {
 formData.append(key, option.data[key]);
 });
 }
 formData.append(option.filename, option.file);
 xhr.onerror = function error(e) {
 option.onError(e);
 };
 xhr.onload = function onload() {
 if (xhr.status < 200 || xhr.status >= 300) {
 return option.onError(getError(action, option, xhr));
 }
 option.onSuccess(getBody(xhr));
 };
 xhr.open('post', action, true);
 if (option.withCredentials && 'withCredentials' in xhr) {
 xhr.withCredentials = true;
 }
 var headers = option.headers || {};
 for (var item in headers) {
 if (headers.hasOwnProperty(item) && headers[item] !== null) {
 xhr.setRequestHeader(item, headers[item]);
 }
 }
 xhr.send(formData);
 return xhr;
 }
}

最后把请求前的校验加上

checkIfCanUpload(){
 return this.fileList.length ? (this.onBefore && this.onBefore() || !this.onBefore) : false;
},

如果父组件定义了onBefore方法且返回了false,或者文件列表为空,请求就不会发送。

相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

推荐阅读:

p5.js使用draw()函数做出黄金螺旋

实战项目中如何使用jquery filter()

热心网友 时间:2022-05-14 21:49

vue-upload-component - Vuejs文件上传组件
vue-core-image-upload - 轻量级的vue上传插件
vue-dropzone - 用于文件上传的Vue组件
声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。
E-MAIL:11247931@qq.com
cdr怎么画曲线箭头 打胎是药流好还是人流好 流产和人流哪个伤害大 打胎吃药好还是人流好 打胎人流好还是药流好 护花使者歌词粤语谐音护花使者这首歌曲歌词 zalo软件怎么转换中文 具体操作步骤 8年以前一个拿扇子的穿的好像是旗袍的欧美女人唱歌后面几个忍者表演歌... 我需要三件红色的T恤衫 翻译 字词句段篇三年级下册答案 linux 中文部分乱码怎么解决 刚用vuejs不久,求一些常用插件,有哪些好用的呢 发票ofd文件怎么打开 海南大学研究生婚育状况证明表上的章应该盖原学校的还是系里的章?求教!!! 已婚育龄妇女婚育状况证明怎么开 大学在校生婚育证明哪里开 关于CAD是什么 建筑电气工程是用什么软件设计的图纸 办独生子女证的婚育状况证明怎么写? 建筑电气工程及其智能化要用什么电脑软件? 画楼房电路图最好的软件是什么? 浩辰电气IDq7.3和浩辰CAD建筑电气有什么区别 绘电气图用什么软件好,CAD感觉用着太麻烦了! 中山大学 研究生新生婚育状况证明卡怎么填 为什么我打不开steam? 我是高职院校建筑电气专业老师,我们可以采购什么CAD软件,学生学起来容易,更容易就业? 国内设计院BIM建筑电气领域主要是用什么软件? vivo X60采用什么处理器? &lt;跪求&gt;建筑电气设计中都用哪种CAD?如何快速入门? vivoX60配置怎么样? 此紫砂壶(底槽青、380cc左右)适合泡岩茶(如大红袍)吗?最适合什么? linux 查询IP时 出现像乱码一样的东西怎么办 vue中开发自己的插件,怎么在插件中引入vue实例 要想茶好喝,泡茶有诀窍,紫砂壶泡岩茶 linux中文乱码 Vue插件定义的全局属性,为什么在vue的实例中看不到 linux查看日志,如何解决字符串乱码 Vue在运行起来后会把插件挂载到什么地方? 如何让vue文件中的代码在Sublime Text 3中高亮和智能提示 一般vue项目中所使用的外部插件都安装在开发环境就可以吗 如果酷狗VIP会员和音乐包到期了,那么在酷狗会员和音乐包期间时下载的已购音乐还会收费吗? 这个月在酷狗开通了付费音乐包后,下个月会自动取消吗 cad视口重叠太多,无法选取编辑视口内图元,怎么办 酷狗开通音乐包总提示连接iTunesstore失败!!求解决方案。跟iTunes有... CAD布局双击进入新复制的视口,点“在当前视口中冻结”,但该新视口中的线没有隐藏掉,只是无法编辑? cad视口编辑问题 CAD布局里的视口怎么复制不了啊?只能复制出一个矩形。我想在同一个布局上复制一个视口进行修改内容。 cad布局窗口复制之后,复制的窗口里面的图形就不能修改了怎么回事? CAD 一个图框,分解不了,看特性居然是个“视口”,里面的图修改不了 选不了 , 怎么办啊 CAD布局窗口不能选择了
  • 焦点

最新推荐

猜你喜欢

热门推荐