当前位置: 首页 > 工具软件 > FileReader.js > 使用案例 >

用element组件配合FileReader实现上传图片并显示,以及过程中遇到的故障onload回调函数中this传递变量失败

慕容光启
2023-12-01

前言

项目中希望我能上传图片,并对图片进行处理,并在预览框显示结果。
对此我采用vue框架中element组件的upload来进行实现:
官方示例demo:Upload 上传

在使用的过程我需要将上传的JPG格式FILE文件转换为base64字符串用于其余业务逻辑处理,而在这过程中我遇到了麻烦(由于自身对于js不熟练),困扰了我数个小时。

这个故障我描述为FileReader.onload绑定的回调函数内无法用this来引用全局变量,对该变量的赋值对外无效

由于印象深刻,我特意记录下来,本篇将分为两部分,一部分是我整套流程中可以通用的代码,另一部分是我遇到这个故障的思考路径和最终解决方案。

提前总结:函数内部和外部所用的this指代内容不相同,因此需要用一个其他变量名传递进去

功能代码

这段代码的实际功能等效于,上传并预览图片,但在预上传函数中,可以获取到图片的字符串格式,在某些情况下或许有用。

控件部分:

<el-upload
	class="avatar-uploader"
	action=""
	:show-file-list="false"
	:on-success="handleAvatarSuccess"
	:before-upload="beforeAvatarUpload">
	<img v-if="imgsrc" :src="'data:image/jpeg;base64,' + imgsrc"  class="avatar">
	<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>

变量部分:

return {
	imgsrc:'',
}

属性部分:

<style scoped>
  .avatar-uploader .el-upload {
    border: 1px dashed #d9d9d9;
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
  }
  .avatar-uploader .el-upload:hover {
    border-color: #409EFF;
  }
  .avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 240px;
    height: 240px;
    line-height: 240px;
    text-align: center;
  }
  .avatar {
    width: 240px;
    height: 240px;
    display: block;
  }
</style>

函数部分:

handleAvatarSuccess(res, file) {
	//this.imageUrl = URL.createObjectURL(file.raw);
},
beforeAvatarUpload(file) {
	const isJPG = file.type === 'image/jpeg';
	const isLt10M = file.size / 1024 / 1024 < 10;

    if (!isJPG) {
    	this.$message.error('上传头像图片只能是 JPG 格式!');         
    }
    if (!isLt10M) {
        this.$message.error('上传头像图片大小不能超过 10MB!');
    }
    if(!(isJPG && isLt10M))
		return isJPG && isLt10M;

	//使用前应该判断一下FileReader是否可用,但我忘了
	let reader = new FileReader();
	
	let _this = this;
	reader.onload = function(e) {                      //先绑定回调
		let base64Str = e.target.result;
		_this.imgsrc = base64Str;
	};
	reader.readAsDataURL (file);                      //再准备读取
	return isJPG && isLt10M;
},

故障探索记录

故障代码原始为:

	reader.onload = function(e) {                      //先绑定回调
		let base64Str = e.target.result;
		this.imgsrc = base64Str;
	};

对比上方代码可以看出我改变的地方,我遇到的故障就是对于this.imgsrc的赋值无效,这使得我对于上传的图片无法预览。

以下是我的探索记录:

改变量名,加入打印观察现象

我先修改了变量名,一方面减少其他因素对此的影响,另一方面方便复原代码环境,再加入打印:

      beforeAvatarUpload(file) {
        const isJPG = file.type === 'image/jpeg';
        const isLt2M = file.size / 1024 / 1024 < 2;

        if (!isJPG) {
          this.$message.error('上传头像图片只能是 JPG 格式!');
        }
        if (!isLt2M) {
          this.$message.error('上传头像图片大小不能超过 2MB!');
        }

        //this.imgsrc2 = "123";
        let reader = new FileReader();
        console.log("flag2.4-----"+this.imgsrc2);
        //this.imgsrc2 = "147";
        reader.onload = function(e) {                      //先绑定回调
          let base64Str = e.target.result;
          console.log("flag2.2-----"+this.imgsrc2);
          this.imgsrc2 = "456";
          console.log("flag2.2-----"+this.imgsrc2);
        };

        reader.readAsDataURL (file);                      //再准备读取

        return isJPG && isLt2M;
      },

我是这么声明这个变量的:

  export default {
    data() {
		return {
		        imgsrc2 : ''
		}
	}
  }

打印信息:

flag2.4-----
flag2.2-----undefined
flag2.2-----456

flag2.4-----
flag2.2-----undefined
flag2.2-----456

分析现象

这一段的出现就表现了问题:flag2.2-----undefined

如果上述函数执行两次,是不应该出现这个问题的(可以确保没有其他地方有对个变量的赋值)

这方面首先我回顾我对vue框架的全局变量的理解,应该是在return 属性中的data部分声明的属性。

我怀疑我对全局变量的理解有错误,但我认为可能不是关键。

我认为声明的方式没有错,使用时用this.imgsrc2进行调用,.vue模板文件中,this关键字可以访问到Vue.prototype上挂载的所有对象,所以写this就相当于写Vue.prototype。

正常情况下是可以赋值的的,但是当进入了函数function(e) 后,就无法进行赋值了。

对此我做出猜测,这个异步的函数很可能跟我正常顺次运行的函数不在同一个时空,因此获取变量对象时,实际获取的是一个镜像变量,虽然同名但是不能对外传递数值。

我使用了watch,但是可以观测到,函数内部进行修改值外部是察觉不到的。

watch: {
      imgsrc2: {
        handler(newOption, oldOption) {
          if(newOption)
            console.log("flag2.5-----"+newOption);
          else
            console.log("flag2.6-----"+oldOption);
        },
        immediate: true,
        deep: true,
      },
    }

这上方函数中的打印没有被执行。

进一步分析并解决

我已经尝试了对变量名进行修改,因此下一步就是修改变量的另一个构成因素this

let _this = this;
 reader.onload = function(e) {                      //先绑定回调
          let base64Str = e.target.result;
          console.log("flag2.2-----"+_this.imgsrc2);
          _this.imgsrc2 = "456";
          console.log("flag2.2-----"+_this.imgsrc2);
        };

(从打印来看赋值成功)

这个方案是我基于猜想时空不同进行的,因为函数内部属于另一个函数,所以导致用this进行定位时和外部的不重合,因此异步时间不同,定位空间不同导致值传递失败,但是现在用一个中间变量将外部环境的this基于中间变量传入内部,使得获取锚点因此可以进行赋值。故障解除。

结语

这个故障用了我大概4小时时间,因为我不太能理解为什么这个变量明明看起来描述的是同一个,但是赋值时却不是同一个,最终我的猜想是内部的函数引用的是外部变量的镜像。如果有高手看到这一篇对于js比较熟悉的,有空请解惑,谢谢。

(积少成多,积少成多)

 类似资料: