Vue的移动端多图上传插件vue-easy-uploader

原文地址

前言

这段时间赶项目,需要用到多文件上传,用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"  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. 支持其他类型文件的上传,可以在组件中自行制定上传的文件类型,及其预览方式。
时间: 2024-07-28 20:06:09

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

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

前段时间,同事又来咨询一个问题了,说手机端动不动拍照就好几M高清大图,上传服务器太慢,问问我有没有可以压缩图片并上传的js插件,当然手头上没有,别慌,我去网上搜一搜. 结果呢,呵呵...诶~又全是基于jquery.zepto的(这句话似乎吐槽次数太多了...),然后我也就不吐槽了, 然后当然是自己做了,先上图: 纯原生js的移动端图片压缩上传插件,不依赖任何库 用法 在html页面中引入input标签,通过自定义属性data-LUploader绑定点击触发的标签id,写法如下: <div cla

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

Bootstrap FileInput 多图上传插件   原文链接:http://blog.csdn.net/misterwho/article/details/72886248?utm_source=itdadao&utm_medium=referral这货融合bootstrap框架,界面相当不错,api非常丰富,可定制内容能满足绝大数的场景. 最近在撸一个项目,用到它,花时间收集了一些文档,整理并翻译了部份内容.备用. github 地址 https://github.com/kartik-

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

作者:白狼 出处:http://www.manks.top/yii2_multiply_images.html 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利. 文件上传也写过几篇文章了,包括最基本的yii2文件上传.异步上传到又拍云以及百度编辑器图片上传的问题,貌似不说点多图上传的就不完美. 今天介绍一款多图上传的插件 FileInput,至于为什么选中了TA作为我们上传的插件,一来这货跟Yii2有一腿,用起来方便:二来嘛

js 移动端 多图上传 预览 传给后端

HTML5定义了 FileReader 作为文件 API 的重要成员用于读取文件,根据 W3C 的定义,FileReader接口提供了读取文件的方法和包含读取结果的事件模型. FileReader的实例拥有 4 个方法,其中 3 个用以读取文件,另一个用来中断读取.下面的表格列出了这些方法以及他们的参数和功能,需要注意的是 ,无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result 属性中. 方法名 参数 描述 abort none 中断读取 readAsBinaryString

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

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

移动端web头像上传实现截取和照片方向修复

实战所需js包: jQuery.Jcrop.EXIF 本次实战功能是在 app 中的 我的客户 的客户信息页面中实现移动端web的头像上传,本次没有实现图像拖拽.缩放的触摸事件功能(Jcrop在这方面的扩展支持实在不够良好,弄了半天没弄出来),若后续有更好的移动端web头像上传插件,可考虑后续替代升级. demo主要实现的关键功能: 图像的方向修正及图像截取 虽然没有实现图像拖拽和双指缩放,但其缩放后的相对于图像的比例计算和拖拽坐标计算规则是一致的,可以参考.同时图像的旋转功能也可参考其中的核心

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

原来上传多图文件,需要挨个选择文件,然后再点批量上传,比较麻烦.所以帝国CMS7.2新增了多图上传插件:为采用FLASH方式实现同时选择多个图片一起上传,提高多图上传效率. 帝国CMS多图上传插件特性如下: 1.采用FLASH方式实现同时选择多个图片一起上传. 2.多图插件安装后,在以下3个地方可以使用: (1).信息上传图片时; (2).图集字段morepic上传图片时; (3).编辑器多图片上传时. 3.上传图片后显示格式采用单独模板文件,用户可自行修改返回格式模板文件,很个性化. 4.本插

PHP服务端如何通过程序将图上传到指定的图片服务器与图片服务器的优化方案

一:PHP服务端如何通过程序将图上传到指定的图片服务器与图片服务器的优化方案 (1) php服务器把图片处理成缩率图或指定大小的图片在通过PHP程序代码 操作FTP 上传到图片服务器. 二:图片服务器的优化方案 (1)用独立的图片服务器处理图片[LigHttpd图片服务器],从处理图片上来看性能要高过Apached和nginx.(2)查看是否是带宽问题导致=>>>确保带宽充足(3)硬盘的IO读写性能[磁盘列阵=>购买多个磁盘] 1)raid0:至少两个硬盘,图片保存在不同的磁盘上.

Java企业微信开发_08_JSSDK多图上传

一.本节要点 1.1可信域名 所有的JS接口只能在企业微信应用的可信域名下调用(包括子域名),可在企业微信的管理后台“我的应用”里设置应用可信域名.这个域名必须要通过ICP备案,不然jssdk会配置失败 1.2JS-SDK使用权限签名算法 1.2.1 签名生成规则如下: (1)参与签名的字段包括: noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) . (2)对所有待签名参数按照字段名的ASCII