前端图片压缩及上传

12,608 阅读4分钟

前言

图片的上传一般情况下不需要上传大体积的图片,因为如果是用户头像或者是一些要求清晰度不是太高的场景上传大体积图片会很消耗资源,一个是上传耗时比较长,同时也增加了存储的开销,当展示的时候也会消耗下载的带宽,影响加载效率。要求用户上传的图片之前压缩图片很影响用户体验,所以就增加了在前端进行图片压缩的需求。

压缩方案

前端图片压缩的主要思路就是将图片绘制到canvas中,然后通过canvas的toDataURL方法来控制图片的质量,对图片进行压缩,另一方面是对图片进行宽高等比缩小来达到图片压缩的效果,下面来看一下代码示例:

 resizeMe(img,type, max_width, max_height) {
        var canvas = document.createElement('canvas');
        var width = img.width;
        var height = img.height;
        max_width = !isNaN(max_width)?max_width:0;
        max_height = !isNaN(max_height)?max_height:0;
        // 在这里图片是等比例缩放的,调用方法时填入图片允许的最大宽度或者是最大的高度
        //如果最大宽度为0 则按照最大高度固定,宽度自适应的方式来实现
        //如果是最大高度为0,则按照最大的宽度来实现
        if(max_width==0){
            if (height > max_height) {
                width = Math.round(width *= max_height / height);
                height = max_height;
            }
        }
        if(max_height==0){
            if (width > max_width) {
                height = Math.round(height *= max_width / width);
                width = max_width;
            }
        }
        canvas.width =width;
        canvas.height = height;
        var ctx = canvas.getContext("2d");
        canvas.width =width;
        canvas.height = height;
        ctx.drawImage(img,0,0, width, height);
        type = type === 'jpg'?"jpeg":type;
        return canvas.toDataURL("image/"+type, 0.7);//这里的0.7值的是图片的质量
  }

在上面的代码中,我们传入的参数主要有image对象,图片类型,图片的最大宽度和最大高度。调用方法时填入图片允许的最大宽度或者是最大的高度,进行等比绘制到canvas中,然后通过toDataURL来转换成base64格式返回,此时的图片就是压缩过后的图片。

创建image对象

上面的示例说了图片压缩的过程,其中有一个参数是image对象,那么这个image对象是如何来的呢。

selectFileImage(el){
     var reader = new FileReader();
    var file = el.target.files[0]
    var fileName = file.name;
    var fileType = file.name.split(".")[1];
        reader.readAsArrayBuffer(file);

        reader.onload = (ev) => {
            var blob = new Blob([ev.target['result']]);
            window['URL'] = window['URL'] || window['webkitURL'];
            var blobURL = window['URL'].createObjectURL(blob);
            var image = new Image();
            image.src = blobURL;
            image.onload = (e) => {
                var thumb = this.resizeMe(image,fileType, 400, 0);//获得的路径是将图片转换成了base64
                axios.post("http://127.0.0.1:3003/useasync/upload",{file:thumb,fileName:fileName}).then(res => {
                    if (res.data.code == 200) {
                       console.log(res)
                    } else {
                        console.log(res)
                    }
                });
            }
        }
    }

在这里面我使用了一个FileReader对象,FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。

当触发input的onChange事件后,可以读取到input中的file文件,于是将此文件读取到缓存当中,当读取完成后,result属性中保存的将是被读取文件的ArrayBuffer数据对象。这里我们可以看一下读取完成后的这个ev到底是个什么东西 前端图片压缩及上传 我们可以看到这里面loaded和total都表示的是文件的总大小,重要的其实是target和currentTarget,这两个属性其实是一样的,里面包含了读取的fileReader对象,里面的result就是缓存中的数据了,我们通过new 一个Blob对象,将其转换为Blob对象,然后就可以通过url方法来将其转换为可以放到img src中的链接形式了。此时创建image对象,并对其src进行赋值,当image加载完成后,就开始调用压缩方法,传入的image对象就是我们刚才生成的image对象。

当压缩完成后返回的数据就是base64的数据了,我们就可以通过ajax异步来进行上传,在此我采用的是axios进行异步上传,将内容及文件名作为参数传递给后台。

后台接收

在这我才用的示例为nodejs搭建后台来接收图片,这里我们需要一个bodyParser模块

app.use(bodyParser.json({ limit:'5mb'}));//限制允许提交的大小

将大小限制为5M以内,也就是说通过base64上传的图片大小一定要小于5M才能成功,这个参数我们可以随意更改,按业务需求而定。

router.post("/upload",function(req,res){
	var imgData = req.body.file;
	var fileName =  req.body.fileName;
    var base64Data = imgData.replace(/^data:image\/\w+;base64,/, "");
    //var size = Buffer.byteLength(base64Data,'base64');
    //var dataBuffer = Buffer.alloc(size,base64Data, 'base64');
    var dataBuffer = Buffer.from(base64Data, 'base64');
    fs.writeFile(process.cwd()+"/upload/"+fileName, dataBuffer, function(err) {
        if(err){
            res.json({success:false,errormsg:err});
        }else{
            res.send({success:true,msg:"保存成功!"});
        }
    });
})

接口中我们通过Buffer来将base64转换为buffer,进而保存到服务器本地中,本示例采用的就是将图片保存到服务器本地。如此通过将base64编码图片保存为图片就做完了。

由于压缩采用的是canvas,获取文件等是通过FileReader 对象及Bolb对象,故此方法目前的兼容性最低为IE10,还请酌情使用。

后记

文章发表自:前端图片压缩及上传