由于页面列表显示较大图片时会导致卡顿或者图片显示加载较慢,给用户带来不好的体验,因此需要在图片上传时进行压缩处理(为什么不在后端多生成一张压缩图?别问,问就是后端没空(╯‵□′)╯︵┻━┻)。产品的需求是将上传图片的最长边压缩到不超过 maxImageSide(例如 1024 像素),本文将介绍如何使用 Vue 3、TypeScript 以及 Element Plus 来实现一个简单的图片上传和压缩功能。
实现步骤
1. 创建上传组件
<template>
<el-upload
:show-file-list="false"
:before-upload="beforeUpload"
accept=".jpg,.jpeg,.png"
>
<el-button type="primary">上传图片</el-button>
</el-upload>
<div v-if="imageSrc">
<img :src="imageSrc" alt="Compressed Image" />
</div>
</template>
2. 上传前校验
- 文件类型和大小验证:在文件上传前,我们首先检查文件类型是否在接受的范围内,并验证文件大小是否超过限制。
/** 上传拦截 */
const beforeUpload = function (file: File) {
const fileType = getFileExtension(file.name)
const isImage = ['.jpg', '.png', '.jpeg'].includes(fileType)
const isAcceptedType = props.accept.includes(fileType)
const isSizeValid = file.size / 1024 / 1024 <= props.size
// 1.文件类型限制.
if (!isAcceptedType) {
ElMessage.error(`上传类型只能为 ${props.accept}`)
return false
}
// 2.文件大小限制
if (!isSizeValid) {
ElMessage.error(`文件大小不能超过 ${props.size}M`)
return false
}
// 3. 非压缩 | 非图片,上传文件
if (!props.thumbnail || !isImage) {
loading.value = true
return true
}
// 4. 压缩图片,限制长边为 maxImageSide
loading.value = true
return compressImage(file)
}
- 压缩逻辑:使用
FileReader读取文件并转换为 Base64 字符串后,通过<canvas>对图像进行压缩。
/** 获取文件后缀 */
const getFileExtension = (fileName: string) => {
return fileName.substring(fileName.lastIndexOf('.')).toLowerCase()
}
/** 压缩图片 */
const compressImage = (file: File) => {
return new Promise(resolve => {
const reader = new FileReader()
reader.onload = event => {
const img = new Image()
img.src = event.target?.result as string
img.onload = () => {
const { width, height } = img
// 若图片长宽不超过maxImageSide,不需压缩
if (width <= maxImageSide && height <= maxImageSide) {
resolve(true)
return
}
const isWidthLonger = width > height
const newWidth = isWidthLonger ? maxImageSide : (width / height) * maxImageSide
const newHeight = isWidthLonger ? (height / width) * maxImageSide : maxImageSide
const canvas = document.createElement('canvas')
canvas.width = newWidth
canvas.height = newHeight
const ctx = canvas.getContext('2d')
ctx?.drawImage(img, 0, 0, newWidth, newHeight)
const dataUrl = canvas.toDataURL(file.type)
const blobData = dataURItoBlob(dataUrl, file.type)
resolve(blobData)
}
}
reader.readAsDataURL(file)
})
}
- 转换为 Blob:将压缩后的 Base64 数据转换为
Blob,以便于上传到服务器。
/** 图片base64转Blob格式 */
function dataURItoBlob(dataURI: string, type: string) {
const binary = atob(dataURI.split(',')[1])
const array = new Uint8Array(binary.length)
for (let i = 0; i < binary.length; i++) {
array[i] = binary.charCodeAt(i)
}
return new Blob([array], { type })
}
实现效果
编辑
以上就是图片上传及压缩的实现啦^-^