最近在学习和工作中遇到这样一个场景:如何将前端上传的图片进行压缩传递给服务端?因为此前从未了解过图片压缩的功能,所以也是带着好奇进行了一番学习。
首先我们要实现input上传文件并做一些细节处理,细节处理要对上传的文件类型和上传的文件大小做限制,类型和大小这里我们可以根据规则自定义。
<div class="container">
<input type="file" id="upload" />
</div>
const ACCEPT = ['image/jpg', 'image/png', 'image/jpeg'] // 所能接受的类型(自定义)
const MAXSIZE = 3 * 1024 * 1024 // 上传图片大小限制(自定义)
const MAXSIZE_STR = '3MB'
const upload = document.getElementById('upload')
upload.addEventListener('change', (e) => {
const [file] = e.target.files
if (!file) return // 上传文件为空时终止
const { type: fileType, size: fileSize } = file
// 类型判断
if (!ACCEPT.includes(fileType)) {
alert('不支持' + fileType + '文件类型')
upload.value = ''
return
}
// 图片大小判断
if (fileSize > MAXSIZE) {
alert(`文件超出${MAXSIZE_STR}限制`)
upload.value = ''
return
}
// 压缩图片
covertImageToBase64(file, (base64Image) =>
compress(base64Image, uploadServer)
)
})
当所有限制都通过之后,就要将图片转为base64格式,也就是进入到covertImageToBase64函数,我们来看看这个函数做了什么:
function covertImageToBase64(file, callback) {
let reader = new FileReader()
reader.readAsDataURL(file) // 读取二进制数据,并将其编码为 base64 的 data url
// 读取完成进行下面操作
reader.addEventListener('load', function (e) {
const base64Image = reader.result
callback && callback(base64Image) // 将读取到的结果传递给回调函数中
reader = null
})
}
这段代码的作用是将文件转换为 base64 编码的数据URL,并通过回调函数返回转换后的结果。它使用了 FileReader 对象来读取文件并进行编码转换。
在这段代码中,covertImageToBase64 函数接受两个参数:file 和 callback。file 参数是要转换的文件对象,callback 是转换完成后的回调函数。
在函数内部,首先创建了一个 FileReader 对象,并调用 readAsDataURL 方法来读取文件并将其编码为 base64 的 data URL。随后,通过添加事件监听器,当读取操作完成时会触发 load 事件,此时获取到转换后的 base64 数据,并将其传递给回调函数中。
需要注意的是,在事件监听器中使用了 reader.result 来获取转换后的 base64 数据,然后将其传递给回调函数。最后,将 reader 对象置为 null,以释放相关资源。
上面的代码都比较好理解,这里较为复杂的逻辑就都在covertImageToBase64函数的的回调compress中,这个功能也是压缩图片中最为核心的部分:
function compress(base64Image, callback) {
let maxW = 800 // 图片最大宽度
let maxH = 800 // 图片最大高度
const image = new Image()
image.src = base64Image
document.body.appendChild(image)
image.addEventListener('load', function (e) {
let ratio // 图片压缩比
let needCompress = false // 是否需要压缩
// 按照比例进行图片宽高的修改
if (image.naturalWidth > maxW ) {
needCompress = true
ratio = image.naturalWidth / maxW
maxH = image.naturalHeight / ratio
}
if (maxH < image.naturalHeight) {
needCompress = true
ratio = image.naturalHeight / maxH
maxW = image.naturalWidth / ratio
}
if (!needCompress) {
maxH = image.naturalHeight
maxW = image.naturalWidth
}
const canvas = document.createElement('canvas')
canvas.setAttribute('id', '__compress__')
canvas.width = maxW
canvas.height = maxH
canvas.style.visibility = 'hidden'
document.body.appendChild(canvas)
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, maxW, maxH)
ctx.drawImage(image, 0, 0, maxW, maxH)
const compressImage = canvas.toDataURL('image/png', 0.9)
callback && callback(compressImage)
// console.log(compressImage)
canvas.remove()
})
}
function uploadServer(compressImage) {
console.log(`upload to server ${compressImage}`)
}
这段代码包含了两个函数,compress 和 uploadServer。compress 函数用于对图片进行压缩处理,并通过回调函数返回压缩后的 base64 图片数据,而 uploadServer 函数则用于将压缩后的图片数据上传至服务器。
在 compress 函数中,首先定义了最大宽度 maxW 和最大高度 maxH,然后创建了一个新的 Image 对象并将 base64 图片数据赋值给其 src 属性,接着将该图片对象添加到 HTML 页面上。随后,通过监听图片的 load 事件来获取图片的自然宽高,并根据设定的最大宽高和图片实际尺寸计算出压缩比例和是否需要压缩。接下来,创建一个用于绘制的 canvas 元素,并将图片绘制到 canvas 上,最后将绘制后的 canvas 转换为 base64 格式的压缩图片,并通过回调函数返回。
在 uploadServer 函数中,直接将压缩后的图片数据打印输出到控制台,这里看起来是一个示例,实际情况下应该将数据上传至服务器端。
要想更好的理解图片压缩,还是要把FileReader
和canvas
的一些api弄清楚,这样才会更加容易理解代码的逻辑,希望这篇文章能够帮助到各位大佬