• 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,总是可以悬崖勒马,让我们浪子回头,其实还是自己的功力不深啊,以后多注重基础,多挖掘细节,以小见大,修炼内功!

    相信有人还会遇到这个问题,希望可以帮助大家,减少不必要的挣扎,记住午饭还是要吃的!
  • 相关阅读:
    【转】HBase中的时间维度
    【转】HBase基本原理
    【转】BloomFilter——大规模数据处理利器
    【转】HBase客户端API:管理特性
    es6 模本字符串拼接方法 ``
    Electron桌面应用打包流程
    vue-electron脚手架安装及说明 打包基于Vue的 桌面应用程序
    safari打开的页面数字识别变为蓝色
    使用jquery中$.each()方法来循环一个数据列表
    jquery实现点击控制div的显示和隐藏
  • 原文地址:https://www.cnblogs.com/lewiscutey/p/9226342.html
Copyright © 2020-2023  润新知