前言
今天分享一下前端如何压缩图片。
一、压缩方法介绍
前端压缩图片,主要是使用了 toDataURL 这个canvas
的方法,其实就是把 canvas
画布上的内容获取到。
这个方法有两个参数,MDN 解释是
参数
type
可选 图片格式,默认为 image/png
encoderOptions
可选
在指定图片格式为 image/jpeg
或 image/webp
的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。
也就是压缩图片只支持上面两种格式(压缩后的格式)
其他格式自行尝试(博主测过png,还压大了。。)
此方法返回一个base64
的图片数据
在上传中使用的是File
或者Blob
格式的数据,因此还需要处理base64
的数据
二、压缩代码
js 方法
// 图片base64数据获取
const photoCompress = (file, compressedOption, callback) => {
let fileReader = new FileReader()
fileReader.readAsDataURL(file)
fileReader.onload = () => {
let fileResult = fileReader.result
canvasDataURL(fileResult, compressedOption, callback)
}
}
// 图片渲染至画布 并获取指定质量图片
const canvasDataURL = (path, compressedOption, callback) => {
let img = new Image()
img.src = path
img.onload = () => {
// 设置压缩后图片规格
let quality = compressedOption.quality
let w = compressedOption.width || img.width
// 判断只存在宽度时,根据比例设置高度
let h = compressedOption.height || (compressedOption.width ? compressedOption.width / (img.width / img.height) : '') || img.height
// 生成canvas
let canvas = document.createElement('canvas')
let ctx = canvas.getContext('2d')
// 设置宽高并渲染图片
canvas.width = w
canvas.height = h
ctx.drawImage(img, 0, 0, w, h)
let base64 = canvas.toDataURL('image/jpeg', quality)
// https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL
// 回调函数返回base64的值
callback(base64)
}
}
// 图片转码处理
const convertBase64UrlToFile = (urlData, filename) => {
let arr = urlData.split(',')
let mime = arr[0].match(/:(.*?);/)[1]
let bstr = atob(arr[1])
// https://www.runoob.com/jsref/met-win-atob.html
let n = bstr.length
let u8arr = new Uint8Array(n)
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
}
// return new Blob([u8arr], { type: mime })
// https://developer.mozilla.org/zh-CN/docs/Web/API/Blob
return new File([u8arr], filename, { type: mime })
// https://developer.mozilla.org/zh-CN/docs/Web/API/File
}
// 获取DOM并注册事件
document.getElementById('inputeFile').addEventListener('change', (e) => {
let file = e.target.files[0]
photoCompress(
file,
{
quality: 0.2,
// width: 0,
// height: 0
},
base64Codes => {
let newFile = convertBase64UrlToFile(base64Codes, file.name)
console.log(newFile)
}
)
})
html标签
<input id="inputeFile" type="file" accept="image/*"/>
convertBase64UrlToFile
函数中的转码,正常使用在上传接口中,iview
、element-ui
的上传组件就用到了上述转码结果的格式。
三、 iview
element-ui
上传处理
iview源码、和element-ui源码 都有一个 before-upload
钩子。
before-upload:上传文件之前的钩子,参数为上传的文件,
若返回 false
或者返回 Promise
且被 reject
,则停止上传。
这里我们需要在这个钩子里返回一个Promise
elementUI 上传组件源码 上传函数代码
upload(rawFile) {
this.$refs.input.value = null;
// 这个就是判断是否通过props传入上传前的钩子
if (!this.beforeUpload) {
return this.post(rawFile);
}
// 我们在这个钩子返回Promise
const before = this.beforeUpload(rawFile);
if (before && before.then) {
before.then(processedFile => {
const fileType = Object.prototype.toString.call(processedFile);
// 上传返回压缩过后的图片文件,这里iview 只支持 File 格式,不支持 Blob 格式
if (fileType === '[object File]' || fileType === '[object Blob]') {
if (fileType === '[object Blob]') {
processedFile = new File([processedFile], rawFile.name, {
type: rawFile.type
});
}
for (const p in rawFile) {
if (rawFile.hasOwnProperty(p)) {
processedFile[p] = rawFile[p];
}
}
this.post(processedFile);
} else {
this.post(rawFile);
}
}, () => {
this.onRemove(null, rawFile);
});
} else if (before !== false) {
this.post(rawFile);
} else {
this.onRemove(null, rawFile);
}
}
iview
和 element-ui
源代码一样,只是 element-ui
更新后兼容Blob
格式,如果使用iview
的 upload
组件,只能 resolve(File)
下面就是我们在项目中要写的代码
<el-upload
class="upload-demo"
:before-update="beforeUpdate">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
beforeUpdate (file, fileList) {
return new Promise((resolve, reject) => {
// 这个压缩函数,就是上面我们写的
photoCompress(
file,
{
quality: 0.2,
// width: 0,
// height: 0
},
base64Codes => {
let newFile = convertBase64UrlToFile(base64Codes, file.name)
resolve(newFile)
}
)
})
}
DEMO演示
http://rudyjoy.cn/compressImage
总结
压缩的方法总体理解起来也不麻烦,完整流程就是, 把原始图片 绘至 canvas。
canvas 使用 toDataURL 方法获取到压缩后的图片数据
最后转码返给 upload 组件使用
以上上传前压缩就写好了,如有疏漏或错误欢迎指正,谢谢。