標籤:tip 理解 直接 raw files java main ble mod
原文地址
前言
這段時間趕項目,需要用到多檔案上傳,用Vue進行前端項目開發。在網上找了不少外掛程式,都不是十分滿意,有的使用起來繁瑣,有的不能適應本項目。就打算自己折騰一下,寫一個Vue的上傳外掛程式,一勞永逸,以後可以直接使用。
目前vue-easy-uploader
已上傳到GitHub
和NPM
,使用起來方便簡單,不需要繁瑣的配置即可投入生產,不過需要後端配合,實現上傳介面。
本項目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 finishedimg_status # 上傳後的路徑反饋數組(資料結構為Set集合)
針對每個state
都有自己的mutation
,用於改變state
,規範上mutation
都需要使用大寫字母加底線的形式,本人習慣使用小寫字母,不過都不是原則上的問題。
最重要的一個state
是img_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
時執行上傳操作。
使用
參考本項目的GItHub和NPM。
注意
使用此外掛程式時,需要與後端約定返回的資料格式,如下:
[{"error":0,"url":"\/uploads\/api\/201711\/25\/fde412bd83d3ec5d6a49769bd0c143cd.jpg"},{"error":0,"url":"\/uploads\/api\/201711\/25\/c6fd51f0388c63a0b6d350331c945fb1.jpg"}]
預覽如下:
返回的是一個上傳後的路徑數組,包括error
和url
欄位,每個檔案有自己的上傳狀態,當error
為0的時候為上傳成功,並返回上傳後的路徑url
改進
後續版本打算進行如下改進
- 把表單的
name
屬性名稱通過組件傳遞。
- 自訂上傳成功後伺服器響應的資料格式,比如自訂
error
的名稱和其值所表示的狀態。
- 支援其他類型檔案的上傳,可以在組件中自行制定上傳的檔案類型,及其預覽方式。
Vue的移動端多圖上傳外掛程式vue-easy-uploader