前端在做上传图片的时候往往需要压缩之后再传给服务器,下面就是通过利用canvas.toDataURL()方法实现压缩图片大小。具体步骤:
- input 读取到文件 ,使用 FileReader 将其转换为 base64 编码
- 新建 img ,使其 src 指向刚刚的 base64
- 新建 canvas ,将 img 画到 canvas 上
- 利用 canvas.toDataURL/toBlob 将 canvas 导出为 base64 或 Blob
- 将 base64 或 Blob 转化为 File
直接上代码:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>上传压缩图片</title>
</head>
<body>
<input type="file" id="input" accept="image/*" onchange="fileChange(event)" multiple>
<img id="img" src="" alt="">
<script>
let fileId = document.getElementById('input')
let img = document.getElementById('img')
let body = document.getElementsByTagName('body')[0]
function creatImg(list) {
const imgBox = document.createElement('div')
list.forEach(item => {
const img = document.createElement('img')
img.src = item.afterSrc
imgBox.appendChild(img)
})
body.appendChild(imgBox)
}
async function fileChange(e) {
let fileList = e.target.files
console.log('file', fileList)
if (fileList.length > 1) {
const resList = await Promise.all(Array.from(fileList).map(e => compressImg(e,
0.1))) // 如果是 file 数组返回 Promise 数组
creatImg(resList)
console.log('resList 多张', resList)
} else {
compressImg(fileList[0], 0.7).then(res => {
console.log('res 单张', res)
img.src = res.afterSrc
})
}
}
/**
* 压缩方法
* @param {string} file 文件
* @param {Number} quality 0~1之间
*/
function compressImg(file, quality) {
return new Promise((resolve) => {
const fileSize = parseInt((file.size / 1024).toFixed(2))
var qualitys = 0.52
if (fileSize < 1024) {
qualitys = 0.85
}
if (5 * 1024 < fileSize) {
qualitys = 0.92
}
if (quality) {
qualitys = quality
}
const reader = new FileReader() // 创建 FileReader
reader.onload = ({
target: {
result: src // src是文件读取完成后解构出的base64图片
}
}) => {
const image = new Image() // 创建 img 元素
image.onload = async () => {
const canvas = document.createElement('canvas') // 创建 canvas 元素
const context = canvas.getContext('2d')
// 根据图片size控制图片宽高,以减少size体积
var targetWidth = image.width
var targetHeight = image.height
var originWidth = image.width
var originHeight = image.height
// 图片在1M到10M之间
if (1 * 1024 <= fileSize && fileSize <= 10 * 1024) {
var maxWidth = 1600
var maxHeight = 1600
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
// 图片在10M到20M之间
if (10 * 1024 <= fileSize && fileSize <= 20 * 1024) {
console.log("图片在10M到20M之间")
maxWidth = 1400
maxHeight = 1400
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
canvas.width = targetWidth
canvas.height = targetHeight
context.clearRect(0, 0, targetWidth, targetHeight)
// 解决背景为透明时背景变黑
context.fillStyle = "#fff"
context.fillRect(0, 0, targetWidth, targetHeight);
context.drawImage(image, 0, 0, targetWidth, targetHeight) // 绘制 canvas
let canvasURL = ""
// 图片小于300kb 不用压缩
if (fileSize < 300) {
canvasURL = canvas.toDataURL()
console.log("图片小于300kb 不用压缩")
} else {
// toDataURL 压缩图片质量,图片类型指定为image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量, 数值越接近1,图片质量越好,图片size也就越大
canvasURL = canvas.toDataURL('image/jpeg', quality)
}
const miniFile = convertBase64ToFile(file, canvasURL)
resolve({
file: miniFile, // 压缩后的file文件
origin: file, // 压缩前file文件
beforeSrc: src, // 压缩前base64图片
afterSrc: canvasURL, // 压缩后base64图片
beforeKB: Number((file.size / 1024).toFixed(2)), // 压缩前的大小
afterKB: Number((miniFile.size / 1024).toFixed(2)) // 压缩后的大小
})
}
image.src = src
}
// readAsDataURL:读取操作完成时,触发loadend事件,同时 result 属性将包含一个base64编码的内容
reader.readAsDataURL(file)
})
}
/**
* 将base64的图片转成文件流形式
* @param {string} file 原文件
* @param {string} base64 压缩后的base64图片编码
*/
function convertBase64ToFile(file, base64) {
// atob:对base64进行解码
const buffer = atob(base64.split(',')[1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
return miniFile
}
</script>
</body>
</html>