由于用户上传的图片大小不固定,为了节约带宽,前端需要做个图片压缩再上传到服务器。
注意:本文中图片压缩方法只能将压缩后的图片固定保存为 “image/jpeg” 格式,不能设置为 “image/png” 格式(该格式压缩无效)。
具体代码如下
压缩相关的方法:
/**
* 文件转base64
* @param {File} file 需要转换的文件
* @param {Function} callback 回调函数, 执行回调后返回 base64 格式图片
*/
export function imageToBase64(file, callback) {
const reader = new FileReader()
reader.readAsDataURL(file) // 文件转base64
reader.addEventListener('load', e => {
callback && callback(e.target.result)
})
}
/**
* base64 类型转 Blob 类型
* @param {base64} base64
* @returns {Blob} blob
*/
export function base64ToBlob(base64) {
let arr = base64.split(','),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
}
/**
* 压缩图片
* @param {base64} originalImage 转换为 base64 的原始图片
* @param {Number} compressRatio 压缩比例 0 - 1, 1则不压缩
* @param {Function} callback 回调方法, 执行回调后返回 base64 格式图片
*/
export function compressImg(originalImage, compressRatio = 1, callback) {
const image = new Image()
image.src = originalImage
/* 监听图片的load事件 */
image.addEventListener('load', function() {
let [sizeRatio, maxWidth, maxHeight] = [0, 1024, 1024] // 图片压缩宽高比例和最大宽高
let [imageWidth, imageHeight] = [this.naturalWidth, this.naturalHeight] // 图片实际宽高
let compressFlag = false // 图片是否需要压缩
// 如果图片宽度大于最大宽度就等比压缩图片的高度
if (imageWidth > maxWidth) {
compressFlag = true
sizeRatio = imageWidth / maxWidth
maxHeight = imageHeight / sizeRatio
}
// 如果图片高度大于最大高度就等比压缩图片的宽度
if (imageHeight > maxHeight) {
compressFlag = true
sizeRatio = imageHeight / maxHeight
maxWidth = imageWidth / sizeRatio
}
// 如果不需要压缩
if (!compressFlag) {
maxWidth = imageWidth
maxHeight = imageHeight
}
// 使用canvas压缩图片
const canvas = document.createElement('canvas')
const ctx = canvas.getContext('2d')
canvas.setAttribute('id', 'canvas')
canvas.width = maxWidth
canvas.height = maxHeight
ctx.clearRect(0, 0, maxWidth, maxHeight) // 清除画布内所有像素
ctx.drawImage(image, 0, 0, maxWidth, maxHeight) // canvas绘制当前图片
const compressImage = canvas.toDataURL('image/jpeg', compressRatio) // 设置压缩类型和压缩比例获取压缩后的文件, 为 base64
callback && callback(compressImage)
})
}
业务中使用,我这里用的 vue,这里只展示了部分代码(大家只需关注 isCompressImg 方法)
beforeRead(file) {
if (file.type !== 'image/jpeg' && file.type !== 'image/png') {
this.$toast('请上传 jpg 或 png 格式图片')
return false
}
return true
},
afterRead(file) {
file.status = 'uploading'
file.message = '上传中...'
this.btnDisabled = true
const imgFile = file.file
this.isCompressImg(imgFile, img => {
// 此时可以自行将文件上传至服务器
alyOSS
.ossUploadFile({ file: img })
.then(res => {
this.fileList = [
{
url: res.url
}
]
file.status = 'done'
file.message = ''
})
.catch(() => {
file.status = 'failed'
file.message = '上传失败'
})
.finally(() => {
this.btnDisabled = false
})
})
},
// 判断是否需要压缩
isCompressImg(file, callback) {
const maxSize = 1 * 1024 * 1024 // 最大 1M
// 压缩图片
if (file.size > maxSize) {
imageToBase64(file, originalImage => {
compressImg(originalImage, 0.5, compressImage => {
// base64 转 blob, blob 再转 file
const img = new File([base64ToBlob(compressImage)], file.name, {
type: file.type
})
// 递归判断, 防止压缩后的图片仍然大于 1M
this.isCompressImg(img, f => {
callback(f)
})
})
})
} else {
callback(file)
}
},