技术实践

JS图片质量压缩上传

最近网站需要上传图片,之前一直是后端上传图片,图片可以控制,由于目前有写作文章以及其他发图功能,图片需要控制质量大小,太大容易造成宽带浪费,

羊先生的头像
羊先生2019.12.25 · 3 分钟阅读 · 159 阅读
JS图片质量压缩上传封面
文章正文还需 3 分钟

这个图片上传压缩功能,就是写的目前这个编辑的图片上传功能,考虑到压缩情况,根据插件提供的API自定义处理上传图片,自定义压缩比列、宽度、高度、压缩最大值、压缩最小值

image.png

整个过程如下

  1. 用户使用file上传图片的时候,用FileReader读取用户上传的图片数据(base64格式)

  2. 把图片数据传入img对象,然后将img绘制到canvas上,再用canvas.toDataURL对图片进行压缩

  3. 获取到压缩后的base64格式图片数据,然后在把base64转成Blob,再通过axios提交formdata至服务器

注意:上传图片服务器需要把base64转成Blob,**Content-Type **需要指定为 multipart/form-data;charset=UTF-8

整体代码代码如下 compress.js

JavaScript 复制代码
class Compress {
    /**
     * quality:0.8 默认压缩质量
     * width 输出图片宽,不指定,原图输出
     * height 输出图片高,不指定,原图输出
     * file 文件对象
     * maxSize 压缩后的最大值 kb
     * minSize 最小压缩值 kb,当图片低于该值时不压缩
     * @param {*} options 
     */
    constructor(options={}){
        this.options = { quality: 0.85, width:'',height:'',file:'',maxSize:500,minSize:60}
        Object.assign(this.options,options);
    }

    // 图片压缩
    photoCompress(){
        let ready = new FileReader();
        /*开始读取指定的Blob对象或File对象中的内容. 当读取操作完成时,readyState属性的值会成为DONE,如果设置了onloadend事件处理程序,
        则调用之.同时,result属性中将包含一个data: URL格式的字符串以表示所读取文件的内容.*/
        ready.readAsDataURL(this.options.file);
        let that = this;
        return new Promise((resolve,reject)=>{
            ready.onload=function(){
                that.canvasDataURL(this.result)
                .then((res)=>{
                    resolve(res)
                })
            }
            ready.onerror = function(){
                reject()
            }
        })
    }
    // canvas 压缩图片
    canvasDataURL(path){
        let img = new Image();
        img.src = path;
        let that = this;
        return new Promise((resolve,reject)=>{
            img.onload = function(){
                // 默认按比例压缩
                let w = this.width,
                    h = this.height,
                    scale = w / h;
                w = that.options.width || w;
                h = that.options.height || (w / scale);
                let quality = 0.7;  // 默认图片质量为0.8
                //生成canvas
                let canvas = document.createElement('canvas');
                let ctx = canvas.getContext('2d');
                // 创建属性节点
                let anw = document.createAttribute("width");
                    anw.nodeValue = w;
                let anh = document.createAttribute("height");
                    anh.nodeValue = h;
                canvas.setAttributeNode(anw);
                canvas.setAttributeNode(anh);
                ctx.drawImage(this, 0, 0, w, h);
                // 图像质量
                if(that.options.quality && that.options.quality <= 1 && that.options.quality > 0){
                    quality = that.options.quality;
                }
                // quality值越小,所绘制出的图像越模糊
                let base64 = canvas.toDataURL('image/jpeg', quality);
                // 回调函数返回base64的值
                resolve(base64)
            }
            img.onerror = function(err){
                reject(err)
            }
        })
    }

     /**
     * 将以base64的图片url数据转换为Blob
     * @param urlData
     * 用url方式表示的base64图片数据
     */
    convertBase64UrlToBlob(urlData){
        var arr = urlData.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});
    }

    // 打开图片压缩
    openPhoto(){
        return new Promise((resolve,reject)=>{
            let size = this.options.file.size / 1024;
            // 当图片小于设置值,不用压缩
            if( size <= this.options.minSize ){
                let formdata = new FormData();
                formdata.append('file',this.options.file,this.options.file.name);
                resolve(formdata)
            }else{
                console.log(`压缩前:${size}kb`);
                this.photoCompress()
                .then((base64Codes)=>{
                    // 转成文件对象 方可上传服务器
                    var bl = this.convertBase64UrlToBlob(base64Codes);
                    console.log(`压缩后:${bl.size / 1024}kb`);
                    var formdata = new FormData();
                    formdata.append("file",bl,Date.parse(new Date())+".jpg");
                    // 判断图片是否符合要求大小
                    if( bl.size / 1000 > this.options.maxSize){
                        reject(formdata)
                    }else{
                        resolve(formdata)
                    }
                })
                .catch((err)=>{
                    reject(err)
                })
            }
        })
    }
}

export default Compress;

utils文件

JavaScript 复制代码
/**
 * 图片压缩
 */
import Compress from '@extend/compress.js'
utils.compress = function(options={}){
    return new Compress(options)
}

vue组件markdown编辑器

JavaScript 复制代码
  <mavon-editor 
	:toolbarsFlag="isView"  
	:subfield="isView" 
	:boxShadow="isView"
	v-model="content.content" 
        @change="change"
        codeStyle="monokai-sublime"
        placeholder="写你知道的"
        ref="md"
        @imgAdd="$imgAdd"
   />

本项目使用如下

JavaScript 复制代码
   //在我的项目使用如下
   let formdata = new FormData();
   formdata.append('file',file,file.name);
   let compress = this.utils.compress({file,maxSize:500});
   compress.openPhoto()
   .then((form)=>{
      // 上传图片接口我已封装
      upimage(form)
         .then((res)=>{
		// md是该编辑器
                this.$refs.md.$img2Url(pos, this.utils.imgsrc(res.path));
           })
           .catch((err)=>{
               // 挂在到原型的提示框
               this.$Message.warning(err);
           })
        })
    .catch((err)=>{
          this.$Message.warning('原文件过大,不超过500kb');
    })

其他同学如若使用

html 复制代码
<input type="file" id="file" name="myfile" accept="image/x-png, image/jpg, image/jpeg, image/gif"/>
JavaScript 复制代码
   import Compress from '@extend/compress.js'
   // files[0] 拿到上传完成之后的文件对象,然后在调用图片压缩
   (new Compress({file:document.getElementById("file").files[0]})).openPhoto()
   .then((form)=>{
      // 压缩完成后的文件对象
   })
   .catch((err)=>{
      //处理压缩失败
   })

总结:
以前觉的上传压缩特麻烦,其实现在看来很简单,理清思路即可,
其他还有直接form表单提交文件等方式,可能不太方便,有兴趣同学可以看看

参考文章
前端图片压缩上传(纯js的质量压缩,非长宽压缩)
HTML5 file API加canvas实现图片前端JS压缩并上传