Vue2.0利用vue-resource上傳文件到七牛

關於上傳,老是有不少能夠說道的。
16年末,公司項目番茄表單的前端部分,開始了從傳統的jqueryvue 2.0的完全重構。可是上傳部分,不管是以前的傳統版本,仍是Vue新版本,都是在使用着FileAPI這款優秀的開源庫,只是進行了簡單的directive化。爲何呢?由於兼容性。沒辦法,公司項目不等同於我的項目,必需要考慮大多數瀏覽器。不然,上傳部分徹底能夠利用Vue-Resource以及FormData來拋開對FileAPI的依賴。這讓我深感遺憾,幸虧這個簡單的遺憾在我的博客Vue化重構的時候得以彌補。javascript

上傳流程

七牛上傳流程

圖不重要看文字
input[type="file"] change事件觸發後,先去(若是是圖片,能夠同時經過FileReader以及readAsDataURL將圖片預覽在頁面上)後臺請求七牛的上傳token,將拿到的tokenkey以及經過change傳遞過來的files一塊兒appendformData中。而後將formData經過post傳遞給七牛,七牛在處理後將返回真正的文件地址css

獲取token

const qiniu = require('qiniu')
const crypto = require('crypto')
const Config = require('qiniu-config')

exports.token = function*() {
    //構建一個保存文件名
    //這裏沒有處理文件後綴,須要本身傳遞過來,而後在這裏處理加在key上,非必須
    const key = crypto.createHash('md5').update(((new Date()) * 1 + Math.floor(Math.random() * 10).toString())).digest('hex')
    //Config 七牛的祕鑰等配置
    const [ACCESS_KEY, SECRET_KEY, BUCKET] = [Config.accessKey, Config.secretKey, Config.bucket] 
    qiniu.conf.ACCESS_KEY = ACCESS_KEY
    qiniu.conf.SECRET_KEY = SECRET_KEY
    const upToken = new qiniu.rs.PutPolicy(BUCKET + ":" + key)
    try {
        const token = upToken.token()
        return this.body = {
            key: key,
            token: token
        }
    } catch (e) {
        // throw error
    }
}

//假設api 地址是 /api/token

上傳組件 upload.vue

<template>
    <label class="mo-upload">
        <input type="file" :accept="accepts" @change="upload">
        <slot></slot>
    </label>
</template>
<style lang="scss">
    .mo-upload {
        display: inline-block;
        position: relative;
        margin-bottom: 0;
        input[type="file"] {
            display: none;
        }
        .mo-upload--label {
            display: inline-block;
            position: relative;
        }
    }
</style>
<script>
    export default {
        name : 'MoUpload',
        props : {
            accepts : { //容許的上傳類型
                type : String,
                default : 'image/jpeg,image/jpg,image/png,image/gif'
            },
            flag : [String, Number], //當前上傳標識,以便於在同一個監聽函數中區分不一樣的上傳域
            maxSize : {
                type : Number,
                default : 0 //上傳大小限制
            }, 
        },
        methods: {
            upload (event) {
                let file = event.target.files[0]
                const self = this
                const flag = this.flag
                if (file) {
                    if (this.maxSize) {
                        //todo filter file
                    }
                    //filter file, 文件大小,類型等過濾
                    //若是是圖片文件
                    // const reader = new FileReader()
                    // const imageUrl = reader.readAsDataURL(file)
                    // img.src = imageUrl //在預覽區域插入圖片

                    const formData = new FormData()
                    formData.append('file', file)
                    
                    //獲取token
                    this.$http.get(`/api/token/`)
                    .then(response => {
                        const result = response.body
                        formData.append('token', result.token)
                        formData.append('key', result.key)
                        //提交給七牛處理
                        self.$http.post('https://up.qbox.me/', formData, {
                            progress(event) {
                                //傳遞給父組件的progress方法
                                self.$emit('progress', parseFloat(event.loaded / event.total * 100), flag) 
                            }
                        })
                        .then(response => {
                            const result = response.body
                            if (result.hash && result.key) {
                                //傳遞給父組件的complete方法
                                self.$emit('complete', 200 , result, flag)
                                //讓當前target能夠從新選擇
                                event.target.value = null
                            } else {
                                self.$emit('complete', 500, result, flag)
                            }
                        }, error => self.$emit('complete', 500, error.message), flag)
                    })
                }
            }
        }
    }
</script>

父組件調用

<template>
    <section>
        ...
        <figure class="upload-preview">
            <img :src="thumbnail" v-if="thumbnail"/>
        </figure>
        <mo-upload flag="'thumbnail'" @complete="uploadComplete" @progress="uploadProgress">
            <a>選擇圖片文件<i class="progress" :style="{width:progress + '%'}"></i></a>
        </mo-upload>
        ...
    </section>
</template>
<script>
    import MoUpload from 'upload'
    export default {
        components : {
            MoUpload,
        },
        data () {
            return {
                thumbnail : null,
                progress : 0 //上傳進度
            }
        },
        methods : {
            uploadProgress (progress, flag) {
                //這裏能夠經過回調的flag對不一樣上傳域作處理
                this.progress = progress < 100 ? progress : 0;
            },
            uploadComplete(status, result, flag) {
                if (status == 200) { //
                    this.thumbnail = `domain.com/${result.key}` //七牛域名 + 返回的key 組成文件url
                } else {
                    //失敗處理
                }
            },
        }
    }
</script>

小結

相比於FILEApi 或者其餘上傳組件,這種方法代碼量最小。可是缺點也是顯而易見的,大量html5 API的使用,勢必會回到兼容性這個老大難上來,慎重的選擇性使用吧html

本文首發於個人博客前端

相關文章
相關標籤/搜索