前端图片压缩库images-quickly-compress

images-quickly-compress

利用canvas画布压缩图片(ES6语法)

安装:
npm install images-quickly-compress --save

使用方式:

import ImagesQuicklyCompress from  "images-quickly-compress";//导出:压缩图片插件
//压缩方式一:
let imageCompress = new ImagesQuicklyCompress({
    mode:'pixel',//根据像素总大小压缩
    num:1E6,//压缩后图片的总像素都是100万(相当于1000px * 1000px的图片)
    size:'500kb',//图片大小超过500kb执行压缩
    imageType:'image/jpeg',//jpeg压缩效果十分理想
    quality:0.8,
    orientation:false,
});

//压缩方式二:
let imageCompress = new ImagesQuicklyCompress({
    mode:'width',//根据固定宽度压缩
    num:500,//压缩后所有图片的宽度都是500px
    size:'500kb',//图片大小超过500k压缩率比较低b执行压缩
    imageType:'image/png', //压缩率比较低
    quality:0.6,
    orientation:false,
});

//注意:files是input的change事件获取的对象
imageCompress.compressor(files).then(res=>{
    console.log('压缩结果:',res);//返回一个blod数组
    let blobArr = res;
    blobArr.forEach(blod => {
        let formData = new FormData();//创建模拟form表单的FormData对象
        formData.append('file',blod);//file是后端接收的变量
        let config = {
            headers:{'Content-Type':'multipart/form-data'}//添加请求头
        };
        axios.post('/api/upload',formData,config).then(response=>{
            //上传图片
            console.log(response.data);
        });
    });
});

参数说明:

参数 说明 是否必须 类型 默认值
mode pixel:是以固定的像素压缩。width:以固定的宽度进行压缩。 String 'pixel'
num pixel模式对应num默认值是100万像素,输出的图片总像素数 1E6 = 宽 * 高<br />width模式对应num的默认值是500,输出的图片宽度都是500px。 Number 1E6
size 对超过该值的图片进行压缩,单位是KB。 String/Number '500kb'或500
imageType 压缩后输入图片的格式:<br />image/jpeg 压缩率比较高,效果十分理想。<br />image/png 压缩率比较低。 String 'image/jpeg'
quality 压缩质量。数值越低压缩后的体积越小,但图片越模糊,建议使用0.8。 Number 0.8
orientation android或ios拍照出来的图片可能会旋转90°/180°/270°,设置为true将图片处理 Boolean false

Vue中使用方式:

<template>
    <div style="position: relative;">
        <div>上传图片</div>
        <UploadImage :multiple="true" :count="10" :before-upload="beforeUpload" :on-success="uploadSuccess"/>
    </div>
</template>

<script>
import UploadImage from '@/components/UploadImage';//引入上传图片的组件。注意路径修改
export default {
    name: "UserAlbum",
    components: {
        UploadImage
    },
    methods: {
        beforeUpload(){
            //上传前的函数,在这里能控制上传图片的数量。
            /*
                同步的处理方式:
                    1. 不返任何值即undefined 允许上传
                    2. return true;//允许上传
                    3. return false;//不允许上传
            */
            //异步的处理方式:
            return new Promise((resolve,reject)=>{
                resolve(true);//只有返回true才允许上传,其它方式都不允许上传
            });
        },
        uploadSuccess(arr){
            //上传成功回调函数
        },
    },
};
</script>

注:UploadImage.vue组件需要安装axios和接口路径需要修改。UploadImage组件是不带任何样式的,使用了absolute绝对定位,使用前需要给它的父标签添加relative相对定位。

<template>
    <div class="UploadImage" @click.stop="upload">
        <div @click.stop="()=>{/*阻止input的点击事件冒泡*/}">
            <input type="file" accept="image/*" :multiple="multiple" :id="inputID"/>
        </div>
    </div>
</template>
<script>
import axios from 'axios';
let imageCompress = new ImagesQuicklyCompress({
    mode: 'pixel', //根据像素总大小压缩
    num: 1E6, //压缩后图片的总像素都是100万(相当于1000px * 1000px的图片)
    size: '500kb', //图片大小超过500kb执行压缩
    imageType: 'image/jpeg', //jpeg压缩效果十分理想
    quality: 0.8
});
export default {
    name: "UploadImage",
    props:{
        count:{
            type:Number,
            default:1,
        },
        multiple:{
            type:Boolean,
            default:false,
        },
        beforeUpload:Function,
        onSuccess: {
            type: Function,
            default: function(){}
        },
    },
    data() {
        let inputID = `id_${Date.now()}_${parseInt(Math.random() * 10000000)}`;
        return {
            inputID:inputID,
            input:null,
        };
    },
    created() {
        
    },
    mounted() {
        this.$nextTick(function() {
            this.input = document.querySelector(`#${this.inputID}`);
            this.input.addEventListener('change',this.change,false);
        });
    },
    beforeDestroy() {
        if(this.input){
            this.input.removeEventListener('change',this.change,false);
        }
    },
    methods: {
        upload(){
            let beforeUpload = this.beforeUpload;
            if(beforeUpload === undefined){
                this.handleInput();
            }else{
                let before = beforeUpload();
                if(before && before.then) {
                    before.then(state=>{
                        if(state === true){
                            this.handleInput();
                        }
                    });
                }else if(before === true || before === undefined){
                    this.handleInput();
                }
            }
        },
        handleInput(){
            //模拟点击input
            this.input.click();
        },
        change(e){
            let files = e.target.files;
            if(files.length === 0)return;
            if(this.count === 0)return;
            imageCompress.compressor(files).then(res=>{
                let all = [];
                let blobArr = res;
                for (let i = 0; i < blobArr.length; i++) {
                    if(i < this.count){//多选超出数量限制的图片,不上传
                        const blod = blobArr[i];
                        let formData = new FormData();//创建模拟form表单的FormData对象
                        formData.append('file',blod);//file是后端接收的变量
                        let p = axios.post('/wxServiceAccount/uploadImageSingle',formData);
                        all.push(p);
                    }
                }
                if(all.length === 0)return;
                Promise.all(all).then(res=>{
                    let arr = res.map(obj=>{
                        if(obj.code === 200)return obj.result;
                    });
                    this.onSuccess(arr);
                    this.input.value = null;//清空就能重复上传
                });
            });
        }
    },
};
</script>
<style scoped lang="scss">
.UploadImage{
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100%;
    height: 100%;
    z-index: 99;
    opacity: 0;
    overflow: hidden;
    input{
        position: absolute;
        top: 110%;
        left: 110%;
    }
}
</style>

React + Ant Design的使用方式:

Upload组件在上传前进行压缩

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import { Upload, message,Modal } from 'antd';
import { LoadingOutlined,PlusOutlined } from '@ant-design/icons';
import ImagesQuicklyCompress from "images-quickly-compress"; //导出:压缩图片插件

let imageCompress = new ImagesQuicklyCompress({
  mode: 'pixel', //根据像素总大小压缩
  num: 1E6, //压缩后图片的总像素都是100万(相当于1000px * 1000px的图片)
  size: '500kb', //图片大小超过500kb执行压缩
  imageType: 'image/jpeg', //jpeg压缩效果十分理想
  quality: 0.8
});

class UploadImage extends Component<any, any> {
  static propTypes: {};
  static defaultProps: { limit: number };
  constructor(props: any) {
    super(props);
    this.state = {
      loading: false,
      fileList: [
        /* {
          uid: '-3',
          url: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
        } */
      ],
    };
  }
  componentDidMount() {
    
  }
  componentWillReceiveProps(props: any) {
    
  }
  async beforeUpload(file:any, fileList:any) {
    //上传前
    if(this.state.fileList.length + fileList.length > this.props.limit){
      message.error(`最多上传${this.props.limit}张图片!`);
      return Promise.reject();
    }
    let [blod] = await imageCompress.compressor([file]);
    blod.uid = file.uid;
    const isLt2M = blod.size / 1024 / 1024 < 2;
    if (!isLt2M) {
      message.error('图片不能超过2MB!');
      return Promise.reject();
    }
    return  Promise.resolve(blod);
  }
  handleChange = (info:any) => {
    //删除与上传 都执行
    
  }
  onPreview(file:any){
    //查看图片
    let src = file.url;
    Modal.info({
      icon:'',
      title: '图片查看',
      okText:'关闭',
      content: (
        <div>
          <img alt="example" style={{ width: '100%' }} src={src} />
        </div>
      )
    });
  };
  
  render() {
    const uploadButton = (
      <div>
        {this.state.loading ? <LoadingOutlined /> : <PlusOutlined />}
        <div className="ant-upload-text">Upload</div>
      </div>
    );
    const { fileList } = this.state;
    return (
        <Upload
          name="image"
          action={this.props.action || "/wxServiceAccount/uploadImageSingle"}
          accept="image/*"
          listType="picture-card"
          className="avatar-uploader"
          fileList={fileList}
          multiple={true}
          beforeUpload={this.beforeUpload.bind(this)}
          onChange={this.handleChange}
          onPreview={this.onPreview}
          headers={{
            token:sessionStorage.getItem('token') || ""
          }}
        >
          {fileList.length < this.props.limit ? uploadButton : ''}
        </Upload>
    );
  }
}

export default UploadImage;


UploadImage.propTypes = {
  name:PropTypes.string.isRequired,
  form:PropTypes.object.isRequired,
  limit:PropTypes.number,//默认值最多上传99张图片
  action:PropTypes.string,
}

UploadImage.defaultProps = {
  limit: 99
}

下面是将压缩后的图片回显到页面,作为知识的扩展,不感兴趣忽略。注:网上有的插件使用base64 URL方式将图片回显到页面,对于体积大的图片,回显过程会明显卡死,但用bold URL能实现秒加载不卡顿。

<!-- HTML标签: -->
<input type="file" accept="image/*" multiple id="imgFilesInput"/>
<!-- <input type="file" accept="image/*" capture="camera" id="fileBtn"/> -->
//js代码:
import ImagesQuicklyCompress from "images-quickly-compress.js";
let imageCompress = new ImagesQuicklyCompress({
    mode:'pixel',
    num:1E6,
    size:'500kb',
    imageType:'image/jpeg',
    quality:0.8
});
document.querySelector('#imgFilesInput').addEventListener("change", function() {
    imageCompress.compressor(this.files).then(res=>{
        console.log('压缩结果:',res);
        let blobArr = res;
        blobArr.forEach(blod => {
            //bold格式的图片显示到页面
            let img = document.createElement('img');
            img.src = window.URL.createObjectURL(blod);
            img.onload = function(){
                document.body.appendChild(img);
                window.URL.revokeObjectURL(this.src);
            }
        });
    });
}, false);

bug反馈:

https://github.com/leforyou/images-quickly-compress/issues

技术参考文档:

https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toBlob

https://developer.mozilla.org/zh-CN/docs/Web/API/Blob

https://developer.mozilla.org/zh-CN/docs/Web/API/URL/createObjectURL

https://www.npmjs.com/package/exif-js

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容