实现思路

通过调整图片的分辨率或者绘图质量来达到图片压缩的效果:

  1. 获取上传 Input 中的图片对象 File

  2. 将图片转换成 base64 格式

  3. base64 编码的图片通过 Canvas 转换压缩,这里会用到的 Canvas 的 drawImage 以及 toDataURL 这两个 Api,一个调节图片的分辨率的,一个是调节图片压缩质量并且输出的

  4. 转换后的图片生成对应的新图片,然后输出


获取上传 Input 中的图片对象 File

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 压缩图片
* @param {File} file : 图片
* @param {Number} maxSize :最大文件大小
*/
export const compressImage = (file, maxSize = 1024 * 1024 * 0.5) => {
if (file.size > maxSize) {
canvasDataURL(file, (blob) => {
console.log('压缩前:' + file.size / 1024 / 1024 + 'M')
console.log('压缩后:' + blob.size / 1024 / 1024 + 'M')
const compresFile = new File([blob], file.name, {
type: 'image/jpeg',
})
// 判断文件是否需要继续压缩
// if (compresFile.size > maxSize) {
// compressImage(compresFile, maxSize);
// } else {
// return compresFile;
// }
return compresFile
})
} else {
return file
}
}

注意:最大文件大小设置过小可能导致无限循环(个人建议为 300kb 以上,最好增加最大循环次数)


通过 canvas 画布实现压缩,并转化为 base64 格式的图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 通过canvas画布实现压缩,并转化为base64格式的图片
* @param {File} file : 图片
* @param {Function} callback :回调函数
*/
function canvasDataURL(file, callback) {
//压缩转化为base64
const reader = new FileReader() //读取文件的对象
reader.readAsDataURL(file) //对文件读取,读取完成后会将内容以base64的形式赋值给result属性
reader.onload = function () {
//读取完成的钩子
const img = new Image()
//先创建canvas画布,再获取canvas画布上的2d绘图环境,通过这个2d绘图环境才可使用绘制API
const canvas = document.createElement('canvas') //创建canvas画布
const drawer = canvas.getContext('2d') //返回一个在画布上绘制2d图的环境对象,该对象上包含有canvas绘制2d图形的API
img.src = this.result
//图片压缩代码,需要注意的是,img图片渲染是异步的,所以必须在img的onlaod钩子中再进行相应操作
img.onload = function () {
const quality = 0.5 // 比例、质量

// // 通过改变图像尺寸实现压缩
// canvas.width = img.width * quality;
// canvas.height = img.height * quality;
// drawer.drawImage(img, 0, 0, canvas.width, canvas.height);
// convertBase64UrlToBlob(canvas.toDataURL("image/jpeg", 1), callback);

// 通过改变图像质量实现压缩
convertBase64UrlToBlob(canvas.toDataURL('image/jpeg', quality), callback)
}
}
}

将 base64 格式转化为 Blob 格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 将base64格式转化为Blob格式
* @param {string} urlData : urlData格式的数据,通过这个转化为Blob对象
* @param {Function} callback : 回调函数
*/
function convertBase64UrlToBlob(urlData, callback) {
const arr = urlData.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1]) //atob方法用于解码base64
// console.log("将base64进行解码:",bstr);
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
callback(
new Blob([u8arr], {
type: mime,
})
)
}

相关插件

安装compression.js

1
npm install compressorjs

引入

1
import Compressor from 'compressorjs'

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
document.getElementById('file').addEventListener('change', (e) => {
const file = e.target.files[0]
if (!file) {
return
}
new Compressor(file, {
quality: 0.6,
success(result) {
const formData = new FormData()
formData.append('file', result, result.name)
axios.post('/path/to/upload', formData).then(() => {
console.log('Upload success')
})
},
error(err) {
console.log(err.message)
},
})
})