一、将 File 对象转成 BASE64 字符串 (FileReader)
<template><div><!-- 用来显示封面的图片 --><!-- <img src="@/assets/images/cover.jpg" alt="" class="cover-img" ref="imgRef" /> --><img :src="previewImg" alt="" class="cover-img" ref="imgRef" /><br /><!-- 文件选择框,默认被隐藏 --><input @change="coverImgChangeHandler" type="file" accept="image/*" ref="iptFileRef" hidden/><!-- 选择封面的按钮 --><button type="text" @click="choosecoverImgHandler">+ 选择封面</button></div>
</template><script>
// ◆导入默认图片, webpack 就会进行打包
import coverImg from '@/assets/images/cover.jpg'
export default {data () {return {// ◆把默认图片赋值给封面图片显示previewImg: coverImg}},methods: {// ◆点击选择封面,触发图片选择框的点击事件choosecoverImgHandler () {this.$refs.iptFileRef.click()},// ◆图片选择框的 change 事件触发coverImgChangeHandler (e) {console.log(e.target.files)// 1.获取用户选择的文件对象const files = e.target.files// 2.判断用户是否选择了文件对象if (files.length === 0) {// 2.1用户没有选择图片(使用默认图片)// 法1// this.$refs.imgRef.src = coverImg// 法2this.previewImg = coverImg} else {// 2.2用户选择了图片(使用选择的图片)// ◆将 File 对象 转成 BASE64 字符串 // 1.创建 FileReader 对象const fr = new FileReader()// 2.调用 readAsDataURL 函数,读取文件内容fr.readAsDataURL(files[0])// 3.监听 fr 的 onload 事件fr.onload = (e) => {// 通过 e.target.result 获取到读取的结果,值是 BASE64 格式的字符串// 法1// this.$refs.imgRef.src = e.target.result// 法2this.previewImg = e.target.result}}}}
}
</script><style lang="less" scoped>
// 设置图片封面的宽高
.cover-img {width: 400px;height: 280px;object-fit: cover;
}
</style>
二、将 File 对象转成 url
<template><div><!-- 用来显示封面的图片 --><img :src="previewImg" alt="" class="cover-img" ref="imgRef" /><br /><!-- 文件选择框,默认被隐藏 --><input @change="coverImgChangeHandler" type="file" accept="image/*" ref="iptFileRef" hidden/><!-- 选择封面的按钮 --><button type="text" @click="choosecoverImgHandler">+ 选择封面</button></div>
</template><script>
// ◆导入默认图片, webpack 就会进行打包
import coverImg from '@/assets/images/cover.jpg'
export default {data () {return {// ◆把默认图片赋值给封面图片显示previewImg: coverImg}},methods: {// ◆点击选择封面,触发图片选择框的点击事件choosecoverImgHandler () {this.$refs.iptFileRef.click()},// ◆图片选择框的 change 事件触发coverImgChangeHandler (e) {console.log(e.target.files)// 1.获取用户选择的文件对象const files = e.target.files// 2.判断用户是否选择了文件对象if (files.length === 0) {// 2.1用户没有选择图片(使用默认图片)this.previewImg = coverImg} else {// 2.2用户选择了图片(使用选择的图片)// 将 File 对象转成 urlthis.previewImg = URL.createObjectURL(files[0])}}}
}
</script><style lang="less" scoped>
// 设置图片封面的宽高
.cover-img {width: 400px;height: 280px;object-fit: cover;
}
</style>
三、总结与思考
总结
- 设置默认图片:将图片作为模块导入,定义变量接收,赋值给图片的
src
- 其他方法:使用自定义指令设置默认图片、在模板中使用 v-if 等
- 点击上传图片按钮,触发图片输入框的
click
事件- 隐藏图片输入框:
hidden
或display:none
- 隐藏图片输入框:
- 绑定图片输入框 的
change
事件,获取文件对象e.target.files
- 判断
e.target.files
的length
- 长度为0:用户取消选择图片,传给后台的数据就是
null
,把默认图片赋值给当前预览区 - 长度为1:用户确认选择图片,把
e.target.files[0]
传给后台
- 长度为0:用户取消选择图片,传给后台的数据就是
- 用户选择了图片之后,预览图片的方法:
- 将获取的文件对象转成
BASE64
字符串:小图片 - 将获取的文件对象转成
url
:大图片
- 将获取的文件对象转成
思考
为什么当用户选择了图片之后,我们不把 e.target.files[0]
直接赋值给预览图片的 src
或 previewImg
,而传给后台就可以?
首先, src
只支持 url
和 BASE64
字符串,而当后台需要的数据类型是 Blob
时,我们就可以直接把 e.target.files[0]
传给它,因为 File
就是 Blob
的子类,关系就像 Array
和 Object
的关系一样。
进阶
一般进行身份认证时会上传身份证正反面,如果这时后端要求将两张身份证图片放到一个数组中进行请求应该如何做到呢?
首先,我们已经通过输入框 的 change
事件,获取到了文件对象 e.target.files
,
e.target.files[0]为单个文件,将拿到的单个文件
push
到新的数组中,以下代码为
vue3
语法实现
//显示图片(图片预览方法)
export const readUrl=(file,preImg)=>{const fr = new FileReader()fr.readAsDataURL(file)fr.onload = (e) => {preImg.value = e.target.result//preImg为预览图片的数据}
}
//此为input框的change事件,以此拿到单个文件
const coverImgChangeHandler= (e)=> {form.value.authFiles.push(e.target.files[0])readUrl(e.target.files[0],previewImg)
}
//提交认证按钮
const submitForm =async (formEl: FormInstance | undefined) => {if (!formEl) returnawait formEl.validate((valid, fields) => {if (valid) {let formData = new FormData();//此处为重点!!记得遍历后再append,直接append报错form.value.authFiles.forEach((file) =>{formData.append('authFiles',file)})// console.log(formData)axios.post('url',formData).then((res) => {console.log(res)}).catch(err => {// console.log(err);})} else {// console.log('提交失败', fields)}})}