elementUI upload 對圖片的寬高做校驗


很開心今天中午沒有吃飯!
原因是一直沒有解決掉一個小問題,於是一直試錯,最后看了下源碼才有了點頭緒,歷時四五個小時才解決掉,有點懷疑自己的能力了,所以寫下此文,記錄一下今天的囧況!
一般情況下遇到問題,自己先是有個思路,然后search answer,如果看到不符合自己思路的直接略過,有點眉目的掃一遍然后試錯,大部分情況下自己的思路是對的,所以解決問題也很快,不幸的是有時候自己的思路一開始就是錯的,沿着一貫的作法只能是屢試屢敗,比如今天遇到的問題就是這樣,所以非常懷疑自己的能力了。

問題:elementUI upload 對圖片的寬高做校驗

一開始我直接百度google,發現都沒有這個問題,這應該是一個很常見的需求啊,element 為啥沒有實現呢,也許是很簡單吧,網上竟然沒有此類問題,我到GitHub的issue里看,確實有類似的問題,但沒有系統的解決方法,涼涼。

我的思路:

1.按照正常的校驗方法推理對寬高的校驗

// 例子中的size和type檢驗
beforeAvatarUpload(file) {
    const isJPG = file.type === 'image/png';
    const isLt2M = file.size / 1024 / 1024 < 2;
    if (!isJPG) {
        this.$message.error('上傳icon只能是 PNG 格式!');
    }
    if (!isLt2M) {
        this.$message.error('上傳icon大小不能超過 2MB!');
    }
    return isJPG && isLt2M;
},
// 推理出的寬高校驗
beforeAvatarUpload(file) {
    const isSize = false;
    const isJPG = file.type === 'image/png';
    const isLt2M = file.size / 1024 / 1024 < 2;
    if (!isJPG) {
        this.$message.error('上傳icon只能是 PNG 格式!');
    }
    if (!isLt2M) {
        this.$message.error('上傳icon大小不能超過 2MB!');
    }
    let img = new Image();
    var _URL = window.URL || window.webkitURL;
    img.onload=function(){
        if(img.width == 96 && img.height == 96) {
            isSize = true;
        } else {
            isSize = false;
        }
    };
    img.src = _URL._URL.createObjectURL(file);
    if (!isSize) {
        this.$message.error('上傳的圖片尺寸只能是100*100!');
    }
    return isJPG && isLt2M && isSize;
},

經過反復的debugger發現img.onload壓根不走,isSize始終是false,看來這種模仿的方法顯然是行不通的,想到onload是異步的,來不及走就return結束了這個方法,所以想法async一下,讓onload之后再執行isSize的判斷以及return,於是有了下面的方法。

2.增加回調函數使onload先執行,然后對isSize有賦值操作

beforeAvatarUpload(file) {
    const isSize = false;
    const isJPG = file.type === 'image/png';
    const isLt2M = file.size / 1024 / 1024 < 2;
    if (!isJPG) {
        this.$message.error('上傳icon只能是 PNG 格式!');
    }
    if (!isLt2M) {
        this.$message.error('上傳icon大小不能超過 2MB!');
    }
    let img = new Image();
    var _URL = window.URL || window.webkitURL;
    img.onload=function(){
        if(img.width == 96 && img.height == 96) {
            isSize = true;
        } else {
            isSize = false;
        }
    };
    img.src = _URL._URL.createObjectURL(file);
    setTimeout(() => {
        if (!isSize) {
            this.$message.error('上傳的圖片尺寸只能是100*100!');
        }
        return isJPG && isLt2M && isSize;
    });
}

現在isSize確實被重新賦值了,有了正確的提示,但只是一閃而過,並成功的上傳了,很郁悶,一步步的開始debugger了,發現最后還是可以回到return的,但是不知道啥原因,接着又開始google了,發現我的思路是錯的。

發現beforeAvatarUpload其實是返回一個Promise

仔細看了下掘金上的這篇文章https://juejin.im/post/59e93cd06fb9a0451968b371,發現upload人家內部確實是想要一個promise,你直接給isSize一個boolean值,最后return出去其實是沒有任何作用的,這就解釋了為何可以彈出錯誤信息卻可以成功上傳了,這個是開始的實現方法。

checkWidthHeight(file) {
    return new Promise((resolve, reject) => {
        let width = 100;
        let height = 100;
        var _URL = window.URL || window.webkitURL;
        var img = new Image();
        img.onload = function() {
            let valid = width === this.width  && height === this.height;
            valid ? resolve() : reject(this);
        }
        img.src = _URL.createObjectURL(file);
    })
},
handleBeforeUpload(file) {
    return this.checkWidthHeight(file).then(async () => {
        isSize = true;
    }, () => {
        isSize = false;
    })
}

我想這應該沒問題了吧,該有的都有了,最后還是利用isSize來確定是否成功檢驗,發現和上次的情況一樣,沒有質的改變,錯誤信息還是一閃而過並可以成功上傳,這下有點慌了,看來沒弄明白為啥需要promise而胡亂的改造,終究解決不了問題,沒有get到人家真正的實現方法,於是看下源碼吧,一看才有點兒眉目。

element UI upload 源碼

// https://github.com/ElemeFE/element/blob/dev/packages/upload/src/upload.vue#L77
upload(rawFile) {
    this.$refs.input.value = null;
    if (!this.beforeUpload) {
    return this.post(rawFile);
    }
    const before = this.beforeUpload(rawFile);
    if (before && before.then) {
    before.then(processedFile => {
        const fileType = Object.prototype.toString.call(processedFile);
        if (fileType === '[object File]' || fileType === '[object Blob]') {
        if (fileType === '[object Blob]') {
            processedFile = new File([processedFile], rawFile.name, {
            type: rawFile.type
            });
        }
        for (const p in rawFile) {
            if (rawFile.hasOwnProperty(p)) {
            processedFile[p] = rawFile[p];
            }
        }
        this.post(processedFile);
        } else {
        this.post(rawFile);
        }
    }, () => {
        this.onRemove(null, rawFile);
    });
    } else if (before !== false) {
         this.post(rawFile);
    } else {
         this.onRemove(null, rawFile);
    }
},

這才發現this.beforeUpload是一個真正的promise,你給人家必須返回一個promise,簡單的boolean值是沒用的,因為人家內部還有很多的對promise的實現,這下清楚了一點,就順水推舟的有了最終的方法,經過多樣測試,的確可以。

最終版代碼

beforeAvatarUpload(file) {
    const isJPG = file.type === 'image/png';
    const isLt2M = file.size / 1024 / 1024 < 2;
    if (!isJPG) {
        this.$message.error('上傳icon只能是 PNG 格式!');
    }
    if (!isLt2M) {
        this.$message.error('上傳icon大小不能超過 2MB!');
    }
    const isSize = new Promise(function(resolve, reject{
        let width = 100;
        let height = 100;
        let _URL = window.URL || window.webkitURL;
        let img = new Image();
        img.onload = function() {
            let valid = img.width >= width && img.height >= height;
            valid ? resolve() : reject();
        }
        img.src = _URL.createObjectURL(file);
    }).then(() => {
        return file;
    }, () => {
        this.$message.error('上傳的icon必須是等於或大於100*100!');
        return Promise.reject();
    });
    return isJPG && isLt2M && isSize;
}

看了最終版的代碼發現確實也很簡單,我們往往被固有的思維帶入歧途,好在有偉大的google,總是可以懸崖勒馬,讓我們浪子回頭,其實還是自己的功力不深啊,以后多注重基礎,多挖掘細節,以小見大,修煉內功!

相信有人還會遇到這個問題,希望可以幫助大家,減少不必要的掙扎,記住午飯還是要吃的!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM