原文地址

前言

这段时间赶项目,需要用到多文件上传,用Vue进行前端项目开发。在网上找了不少插件,都不是十分满意,有的使用起来繁琐,有的不能适应本项目。就打算自己折腾一下,写一个Vue的上传插件,一劳永逸,以后可以直接使用。

目前vue-easy-uploader已上传到GitHubNPM,使用起来方便简单,不需要繁琐的配置即可投入生产,不过需要后端配合,实现上传接口。

本项目GitHub地址: https://github.com/quanzaiyu/vue-easy-uploader

本项目NPM地址: https://www.npmjs.com/package/vue-easy-uploader

详细的使用方法都在仓库Readme中,就不赘述,这里谈下本插件的设计开发思路。

插件介绍

vue-easy-uploader是一个多图上传插件。主要特性包括:

  • 多文件上传
  • 上传图片预览
  • 上传状态监测
  • 删除指定图片
  • 清空图片
  • 重新上传

后期版本迭代将不限于图片,往通用文件上传进行改进。

先看看上传插件使用时候的效果图:

目录结构

index.js # 主入口文件
store.js # 状态管理
uploader.vue # 上传组件

文件解析

index.js

import uploader from './uploader'
import store from './store' let plugin = {} plugin.install = function (_Vue, _store) {
_Vue.component('uploader', uploader)
_store.registerModule('imgstore', store)
} export default plugin

这是插件的主入口文件,注册了全局的上传组件和状态管理,使用时只需要在项目入口文件(一般是main.js)中加入以下代码即可引入此插件:

import Vue from 'vue'
import Vuex from 'vuex'
import uploader from 'vue-easy-uploader' let store = new Vuex.Store({})
Vue.use(uploader, store)

store.js

此文件为状态管理配置文件,主要包含三个state:

img_upload_cache # 上传文件缓存
img_paths # 上传状态,包括 ready selected uploading finished
img_status # 上传后的路径反馈数组(数据结构为Set集合)

针对每个state都有自己的mutation,用于改变state,规范上mutation都需要使用大写字母加下划线的形式,本人习惯使用小写字母,不过都不是原则上的问题。

最重要的一个stateimg_status,用于监视图片上传的状态。包括以下几个状态:

ready # 上传开始前的准备状态
selected # 已选择上传文件
uploading # 开始上传
finished # 上传完毕

在组件中可以通过改变上传状态实现文件的上传,同时也可以监听上传状态的变化而执行回调。如:

methods: {
upload () {
this.$store.commit('set_img_status', 'uploading')
},
submit () {
// some code
}
}
computed: {
...mapState({
imgStatus: state => state.imgstore.img_status
})
},
watch: {
imgStatus () {
if (this.imgStatus === 'finished') {
this.submit()
}
}
}

上述代码中,使用upload方法更新了上传状态,让图片开始执行上传操作,使用watch进行上传状态的监视,当上传完成(img_status状态变为finished),执行回调函数submit

源文件如下:

// Created by quanzaiyu on 2017/10/25 0025.
var state = {
img_upload_cache: [],
img_paths: [],
img_status: 'ready' // 上传状态 ready selected uploading finished
} const actions = {} const getters = {} const mutations = {
set_img_upload_cache (state, arg) {
state.img_upload_cache = arg
},
set_img_paths (state, arg) {
state.img_paths = arg
},
set_img_status (state, arg) {
state.img_status = arg
}
} export default {
state,
mutations,
actions,
getters
}

uploader.vue

先看源代码(为了节省空间,未贴出style部分的代码):

<template>
<div class="imgUploader">
<div class="file-list">
<section
v-for="(file, index) in imgStore" :key="index"
class="file-item draggable-item"
>
<img :src="file.src" alt="" ondragstart="return false;">
<span class="file-remove" @click="remove(index)">+</span>
</section>
<section class="file-item" v-if="imgStatus !== 'finished'">
<div class="add">
<span>+</span>
<input type="file" pictype='30010003' multiple
data-role="none" accept="image/*"
@change="selectImgs"
ref="file"
>
</div>
</section>
</div>
<div class="uploadBtn">
<section>
<span v-if="imgStore.length > 0" class="empty"
@click="empty">
{{imgStatus === 'finished' ? '重新上传' : '清空'}}
</span>
</section>
</div>
</div>
</template> <script>
import { mapState } from 'vuex'
export default {
props: ['url'],
data () {
return {
files: [], // 文件缓存
index: 0 // 序列号
}
},
computed: {
...mapState({
imgStore: state => state.imgstore.img_upload_cache,
imgPaths: state => state.imgstore.img_paths,
imgStatus: state => state.imgstore.img_status
})
},
methods: {
// 选择图片
selectImgs () { # ①
let fileList = this.$refs.file.files
for (let i = 0; i < fileList.length; i++) {
// 文件过滤
if (fileList[i].name.match(/.jpg|.gif|.png|.bmp/i)) {
let item = {
key: this.index++,
name: fileList[i].name,
size: fileList[i].size,
file: fileList[i]
}
// 将图片文件转成BASE64格式
let reader = new FileReader() # ②
reader.onload = (e) => {
this.$set(item, 'src', e.target.result)
}
reader.readAsDataURL(fileList[i])
this.files.push(item)
this.$store.commit('set_img_upload_cache', this.files) // 存储文件缓存
this.$store.commit('set_img_status', 'selected') // 更新文件上传状态
}
}
},
// 上传图片
submit () {
let formData = new FormData() # ③
this.imgStore.forEach((item, index) => {
item.name = 'imgFiles[' + index + ']' # ④
formData.append(item.name, item.file)
})
formData.forEach((v, k) => console.log(k, ' => ', v))
// 新建请求
const xhr = new XMLHttpRequest() # ⑤
xhr.open('POST', this.url, true)
xhr.send(formData)
xhr.onload = () => {
if (xhr.status === 200 || xhr.status === 304) {
let datas = JSON.parse(xhr.responseText)
console.log('response: ', datas)
// 存储返回的地址
let imgUrlPaths = new Set() # ⑥
datas.forEach(e => { // error === 0为成功状态
e.error === 0 && imgUrlPaths.add(e.url)
})
this.$store.commit('set_img_paths', imgUrlPaths) // 存储返回的地址
this.files = [] // 清空文件缓存
this.index = 0 // 初始化序列号
this.$store.commit('set_img_status', 'finished') // 更新文件上传状态
} else {
alert(`${xhr.status} 请求错误!`)
}
}
},
// 移除图片
remove (index) {
this.files.splice(index, 1)
this.$store.commit('set_img_upload_cache', this.files) // 更新存储文件缓存
},
// 清空图片
empty () {
this.files.splice(0, this.files.length)
this.$store.commit('set_img_upload_cache', this.files) // 更新存储文件缓存
this.$store.commit('set_img_paths', [])
}
},
beforeCreate () {
this.$store.commit('set_img_status', 'ready') // 更新文件上传状态
},
destroyed () {
this.$store.commit('set_img_upload_cache', [])
this.$store.commit('set_img_paths', [])
},
watch: {
imgStatus () {
if (this.imgStatus === 'uploading') {
this.submit() # ⑦
}
},
imgStore () {
if (this.imgStore.length <= 0) {
this.$store.commit('set_img_status', 'ready') // 更新文件上传状态
}
}
}
}
</script> <style lang="less" scoped>
...
</style>

以上代码中有一些注释序号,是此插件设计的主要思路,其他代码都比较容易理解,分别说下

  • ① 选择文件后执行,img_status状态变为selected
  • ② 将带上传的图片文件转化为Base64格式,用于缩略图显示。
  • ③ 创建一个表单对象,用于存储待上传的文件。
  • ④ 注意这里的name属性值,暂时写死,后面设计打算从组件中指定name属性,如果是多文件的话,name属性的数组序号从0开始递增。
  • ⑤ 未依赖任何Ajax请求插件,使用原生的XMLHttpRequest对象创建请求。
  • ⑥ 存储上传成功后服务器返回的上传路径。
  • ⑦ 检测上传状态,当在使用此插件时将img_status的状态设置为uploading时执行上传操作。

使用

参考本项目的GItHubNPM

注意

使用此插件时,需要与后端约定返回的数据格式,如下:

[{"error":0,"url":"\/uploads\/api\/201711\/25\/fde412bd83d3ec5d6a49769bd0c143cd.jpg"},{"error":0,"url":"\/uploads\/api\/201711\/25\/c6fd51f0388c63a0b6d350331c945fb1.jpg"}]

预览如下:

返回的是一个上传后的路径数组,包括errorurl字段,每个文件有自己的上传状态,当error为0的时候为上传成功,并返回上传后的路径url

改进

后续版本打算进行如下改进

  1. 把表单的name属性名称通过组件传递。
  2. 自定义上传成功后服务器响应的数据格式,比如自定义error的名称和其值所表示的状态。
  3. 支持其他类型文件的上传,可以在组件中自行制定上传的文件类型,及其预览方式。

Vue的移动端多图上传插件vue-easy-uploader的更多相关文章

  1. 纯原生js移动端图片压缩上传插件

    前段时间,同事又来咨询一个问题了,说手机端动不动拍照就好几M高清大图,上传服务器太慢,问问我有没有可以压缩图片并上传的js插件,当然手头上没有,别慌,我去网上搜一搜. 结果呢,呵呵...诶~又全是基于 ...

  2. Bootstrap FileInput 多图上传插件 文档属性说明

    Bootstrap FileInput 多图上传插件   原文链接:http://blog.csdn.net/misterwho/article/details/72886248?utm_source ...

  3. springBoot+ vue+ Element-ui实现合并多图上传(一次请求多张图片)

    这次上传使用的是Elemet-ui的uoload上传组件,组件预留的钩子回调还是比较充足的. 1:  实现多图上传主要用到以下两个属性: 下面讲一下属性使用: <el-upload :actio ...

  4. yii2组件之多图上传插件FileInput的详细使用

    作者:白狼 出处:http://www.manks.top/yii2_multiply_images.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连 ...

  5. js 移动端 多图上传 预览 删除 base64转为url 传给后端

    说下主要的逻辑,首先是利用input type="file",上传文件,然后判断文件类型是否是图片,这里要注意(multiple,安卓一次一张,ios可以多张). 接着把本地图片转 ...

  6. swfupload多图上传插件(ASP.NET)

    <script src="../js/swfupload/swfupload.js" type="text/javascript"></scr ...

  7. 帝国CMS7.2新增多图同时上传插件,上传多图效率更高

    原来上传多图文件,需要挨个选择文件,然后再点批量上传,比较麻烦.所以帝国CMS7.2新增了多图上传插件:为采用FLASH方式实现同时选择多个图片一起上传,提高多图上传效率. 帝国CMS多图上传插件特性 ...

  8. PHP 多图上传,图片批量上传插件,webuploader.js,百度文件上传插件

    PHP  多图上传,图片批量上传插件,webuploader.js,百度文件上传插件(案例教程) WebUploader作用:http://fex.baidu.com/webuploader/gett ...

  9. 基于vue + axios + lrz.js 微信端图片压缩上传

    业务场景 微信端项目是基于Vux + Axios构建的,关于图片上传的业务场景有以下几点需求: 1.单张图片上传(如个人头像,实名认证等业务) 2.多张图片上传(如某类工单记录) 3.上传图片时期望能 ...

随机推荐

  1. 在imagenet预训模型上进行finetune

    所谓fine tune就是用别人训练好的模型,加上我们自己的数据,来训练新的模型.fine tune相当于使用别人的模型的前几层,来提取浅层特征,然后在最后再落入我们自己的分类中. fine tune ...

  2. 面向对象特征:封装、多态 以及 @propetry装饰器

    (继承补充)组合 obj=fun()#对象 obj.attr=foo()#对象的属性等于另一个对象 什么是组合:     A类的对象具备某一个属性,该属性的值是B类的对象   基于这种方式就把A类与B ...

  3. centos7.2环境中kettle环境搭建及任务推送配置详解

    目标:将mysql5.5中testdb1的ehr_user表推送到tdoa的ehr_user表中,为避免不必要的麻烦,两张表结构.编码,包括数据库编码保持一致 操作系统:centos7.2 kettl ...

  4. Android开源动画库nineoldandroids

    项目官网地址:http://nineoldandroids.com/ 使用这个库的原因是android3.0之后出了新的animation API,但是android3.0以下的不支持 这个库完成了这 ...

  5. python3内存存储几种数据类型对差异

    列表,元组,集合,字典几种数据类型差异 列表: list=[0,1,'a'] 元组:list=(0,1,'a') 集合 :list=[0,1,'a'] 字典:list={name:'tom',age: ...

  6. as 插件GsonFormat用法(json字符串快速生成javabean)

    GsonFormat 主要用于使用Gson库将JSONObject格式的String 解析成实体,该插件可以加快开发进度,使用非常方便,效率高. 插件地址:https://plugins.jetbra ...

  7. 转:vue+canvas如何实现b站萌系登录界面

    https://juejin.im/post/5ae802a46fb9a07ac55fec04

  8. STM32F412应用开发笔记之九:移植FreeRTOS到F412ZG平台

    在开发实际应用系统时,我们经常需要考虑数据的实时性和多任务,嵌入式实时操作系统的出现为实现这一目的提供了很好的助力.FreeRTOS是近年来比较流行的嵌入式实时操作系统,而且是开源免费的,STM32C ...

  9. 【ES】代码例子

    #!/usr/bin/env python #coding=utf-8 from elasticsearch import Elasticsearch from elasticsearch_dsl i ...

  10. python 全栈开发,Day10(动态参数,命名空间,作用域,函数嵌套)

    一.动态参数 def func(a,b,c,d,e,f,g): pass func(1,2,3,4,5,6,7) 如果加30个参数呢?有没有万能的参数,可以代表一切参数呢? *args 动态参数,万能 ...