前端实现本地图片读取与简单压缩功能
在上一篇文章 Javascript 基础夯实 —— 通过代码构建一个包含文件的 FormData 对象 中提到了前端压缩图片的功能,所以本篇文章就来实现一下这个功能
前端获取本地图片文件
<input id="input" type="file" accept="image/*" >
通过一个类型为file的input标签,我们可以获取到设备本地的文件,input还可以声明一个accept的属性,这个属性用来过滤input可以选择的文件,如果不声明则可以选择所有文件
在这里,accept的值是image/*,这表示input可以选择所有类型的图片文件,包括 png/jpg/jpeg/gif/bmp 等等,如果需要限制可以选择的文件类型,则可以改写成这样:
// input 仅可以选择 png/jpg/jpeg 格式的图片文件
<input id="input" type="file" accept="image/png, image/jpg, image/jpeg" >
如果需要多选,还可以再声明一个multiple属性
onchange 事件与获取选择的文件
我们可以通过onchange事件监听到input状态的改变,这样就能在选择完文件后,对文件进行操作
input元素有一个files属性,这个属性的值是一个文件对象数组,用来保存当前选择过的文件
const $input = document.getElementById('input')
$input.onchange = e => {
// 文件对象数组
console.log($input.files)
}
读取文件对象内容
虽然获取到了选择的图片文件,但是我们并不能对 File 对象直接进行压缩的操作,而是需要先读取 File 对象的内容,再对读取到的内容进行操作
读取 File 对象的内容,我们可以通过 FileReader 对象来实现,此处以选择的第一张图片为例:
$input.onchange = e => {
// 获取到第一个文件
const file = $input.files[0]
// 声明一个 FileReader 实例
const fileReader = new FileReader()
// fileReader 读取完成触发的事件
fileReader.onload = () => {
// 打印 file 的读取结果
console.log(fileReader.result)
}
// 读取 file 的内容
fileReader.readAsDataURL(file)
}
FileReader 对象上有以下属性及方法:
-
属性:
- readyState:FileReader 当前的状态,有三种值,0-还未开始读取数据;1-正在读取数据;2-数据读取完成
- result:在读取完成后才会存在的属性,值是读取到的文件的内容
- onload:文件读取完成后触发的事件
- error:读取文件时的错误信息
-
常用方法:
- readAsDataURL:将 File 或 Blob 读取为一个 base64 编码的 URL 字符串
- readAsText:读取 File 或 Blob 的文本内容,第二个参数可以指定编码类型,默认 utf-8
- abort:取消读取的操作
需要注意的是,
readAsDataURL和readAsText都是异步的,必须监听readyState或者在onload事件中才能获取到读取到的结果,所以出现多个文件需要遍历读取的情况时,需要特别注意
在上面的代码中,将图片文件读取为了一个 base64 编码的 URL 字符串,下面就可以通过这个字符串来创建一个 Image 对象了:
fileReader.onload = () => {
const image = new Image()
image.onload = () => {
// 对 image 进行的操作
}
image.src = fileReader.result
}
拿到图片文件生成的 image 对象,下面就可以正式进行图片的压缩了!
前端压缩图片的实现
压缩的实现又要借助到 canvas 画布元素。先来说一下原理,再来实现功能
原理简述
首先需要先有一个 canvas 来绘制图片,这个 canvas 的宽高需要设置为我们最终图片压缩后的宽高,比如将一张 19201920 的图片压缩到 300300 的尺寸,那就要将 canvas 的尺寸设置为 300*300
然后再将图片按比例缩放绘制到 canvas 上,再将 canvas 的上下文导出为一个 base64 的 url,导出的过程中我们可以设定导出的压缩比率和导出的图片格式
最终我们拿到了一个压缩后的图片的 base64 编码的 url,我们可以将这个 url 转为 Blob 对象,再通过表单的方式传输到后台。关于这一步,在开头的链接中,也就是上一篇文章已经说过了,在本文中不再赘述:
代码实现
image.onload = () => {
const imageUrl = compressImage (image)
console.log(imageUrl)
// 拿到 base64 URL 后,就可以将其转为 Blob 对象
}
function compressImage (image) {
// 创建一个 canvas 元素并获取其上下文
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
// 将 canvas 宽高设为原图的 1/10,即将原图宽高压缩 10 倍
canvas.width = image.width / 10
canvas.height = image.height / 10
// 绘制图片
ctx.drawImage(image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height)
// 将 canvas 导出为 base64 URL 并返回
return canvas.toDataURL('image/jpeg', 0.1)
}
需要注意的几点:
-
drawImage()方法是 canvas 上下文环境的方法,而不是 canvas 元素的方法,这个方法可以接收多个参数,并且参数长度不同,作用也不同,在这里的作用是:将 image 从 (0, 0) 的位置开始截取一个宽高为 image.width, image.height 的图像(即将图像完整截取),放置在 canvas 上从 (0, 0) 开始,到 canvas.height, canvas.width 的区域中(也就是完整缩放在 canvas 中)。当传入其他数量参数时,小伙伴们可以参考这个页面:HTML DOM drawImage() 方法 - 导出图像方法
toDataURL是 canvas 的方法,第一个参数hi导出的格式,不传或者传入错误格式的话,会默认使用 png 格式;第二个参数是导出的品质系数,范围为 0-1,默认 0.92,但是这个系数只对导出类型为 jpeg 和 webp 的图片生效
扫码关注前端周记公众号