lizuncong (lizuncong) · GitHubI am a strong believer in reverse engineering. lizuncong has 42 repositories available. Follow their code on GitHub.https://github.com/lizuncong

需求:前端上传图片的时候通常需要提供指定大小以内的图片。比如不大于500KB。

思路:利用canvas转blob的时候通过quality控制图片质量,达到压缩的目的。此方法有个缺点。只能对图片格式为jpeg或webp的图片有效。因此压缩的时候canvas.toBlob(callback, mimeType, quality)中的mimeType要设为'image/jpeg'。压缩完成可以自行转成想要的格式。这里最主要的是找到小于maxSize并且最接近maxSize的图片质量参数quality。

效果图:用进度条模拟压缩的进度。支持同时上传多张图片同时压缩

代码如下:

import React from 'react';
import PropTypes from 'prop-types';
import styles from './upload.less';import compress from './compress';class Upload extends React.Component {constructor(props) {super(props);this.fileInput = React.createRef();this.state = {fileObjs: [], // item { originFile, compressBase64, compressFile }};}getFileUrl(file) {let url;const agent = navigator.userAgent;if (agent.indexOf('MSIE') >= 1) {url = file.value;} else if (agent.indexOf('Firefox') > 0 || agent.indexOf('Chrome') > 0) {url = window.URL.createObjectURL(file);}return url;}compressCallBack(file, fileObj, result) {const { fileObjs } = this.state;file.compressing = false; // 压缩完成fileObj.compressBase64 = result.compressBase64;fileObj.compressFile = result.compressFile;this.setState({ fileObjs: [...fileObjs] });if (fileObjs.length && fileObjs.every(fileObjItem => fileObjItem.compressBase64)) {console.log('全部压缩完成', fileObjs);}}onInputChange(e) {const { fileObjs } = this.state;Object.keys(e.target.files).forEach((key) => {const file = e.target.files[key];// 验证图片格式const type = file.name.split('.')[1];if (type !== 'png' && type !== 'jpg' && type !== 'jpeg') {console.warn('请上传png,jpg,jpeg格式的图片!');e.target.value = '';return;}file.url = this.getFileUrl(file);file.compressing = true; // 压缩状态,开始压缩const fileObj = { originFile: file, compressBase64: null, compressFile: null };fileObjs.push(fileObj);// 压缩图片的方法, maxSize单位为kbcompress(file, 200).then((res) => {this.compressCallBack(file, fileObj, res);}, (err) => {// 压缩失败,则返回原图片的信息this.compressCallBack(file, fileObj, err);});});this.setState({ fileObjs: [...fileObjs] });e.target.value = '';}render() {const { fileObjs } = this.state;return (<divclassName={styles.uploadContainer}><div className={styles.gridItem}><divclassName={styles.inputContainer}onClick={() => {this.fileInput.current.click();}}><span className={styles.uploadIcon}>+</span><inputclassName={styles.fileInput}ref={this.fileInput}type="file"name="file"multiple="multiple"accept="image/*"onChange={e => this.onInputChange(e)}/></div></div>{fileObjs.map(fileObj => (<div className={styles.gridItem}><imgsrc={fileObj.compressBase64 ? fileObj.compressBase64 : fileObj.originFile.url}className={fileObj.originFile.compressing && styles.filter}/>{fileObj.originFile.compressing ?<div className={styles.progressContainer}><div className={styles.progress}><div className={styles.progressHighlight} /></div></div> : ''}</div>))}</div>);}
}Upload.propTypes = {dispatch: PropTypes.func.isRequired,
};export default Upload;

2.图片压缩主要代码compress.js


// 将File(Blob)对象转变为一个dataURL字符串, 即base64格式
const fileToDataURL = file => new Promise((resolve) => {const reader = new FileReader();reader.onloadend = e => resolve(e.target.result);reader.readAsDataURL(file);
});// 将dataURL字符串转变为image对象,即base64转img对象
const dataURLToImage = dataURL => new Promise((resolve) => {const img = new Image();img.onload = () => resolve(img);img.src = dataURL;
});// 将一个canvas对象转变为一个File(Blob)对象
const canvastoFile = (canvas, type, quality) => new Promise(resolve => canvas.toBlob(blob => resolve(blob), type, quality));const compress = (originfile, maxSize) => new Promise(async (resolve, reject) => {const originSize = originfile.size / 1024; // 单位为kbconsole.log('图片指定最大尺寸为', maxSize, '原始尺寸为:', originSize);// 将原图片转换成base64const base64 = await fileToDataURL(originfile);// 缩放图片需要的canvasconst canvas = document.createElement('canvas');const context = canvas.getContext('2d');// 小于maxSize,则不需要压缩,直接返回if (originSize < maxSize) {resolve({ compressBase64: base64, compressFile: originfile });console.log(`图片小于指定大小:${maxSize}KB,不用压缩`);return;}const img = await dataURLToImage(base64);const scale = 1;const originWidth = img.width;const originHeight = img.height;const targetWidth = originWidth * scale;const targetHeight = originHeight * scale;canvas.width = targetWidth;canvas.height = targetHeight;context.clearRect(0, 0, targetWidth, targetHeight);context.drawImage(img, 0, 0, targetWidth, targetHeight);// 将Canvas对象转变为dataURL字符串,即压缩后图片的base64格式// const compressedBase64 = canvas.toDataURL('image/jpeg', 0.1);// 经过我的对比,通过scale控制图片的拉伸来压缩图片,能够压缩jpg,png等格式的图片// 通过canvastoFile方法传递quality来压缩图片,只能压缩jpeg类型的图片,png等格式不支持// scale的压缩效果没有canvastoFile好// 在压缩到指定大小时,通过scale压缩的图片比通过quality压缩的图片模糊的多// 压缩的思路,用二分法找最佳的压缩点// 这里为了规避浮点数计算的弊端,将quality转为整数再计算;// const preQuality = 100;const maxQualitySize = { quality: 100, size: Number.MAX_SAFE_INTEGER };const minQualitySize = { quality: 0, size: 0 };let quality = 100;let count = 0; // 压缩次数let compressFinish = false; // 压缩完成let invalidDesc = '';let compressBlob = null;// 二分法最多尝试8次即可覆盖全部可能while (!compressFinish && count < 12) {compressBlob = await canvastoFile(canvas, 'image/jpeg', quality / 100);const compressSize = compressBlob.size / 1024;count++;if (compressSize === maxSize) {console.log(`压缩完成,总共压缩了${count}次`);compressFinish = true;return;}if (compressSize > maxSize) {maxQualitySize.quality = quality;maxQualitySize.size = compressSize;}if (compressSize < maxSize) {minQualitySize.quality = quality;minQualitySize.size = compressSize;}console.log(`第${count}次压缩,压缩后大小${compressSize},quality参数:${quality}`);quality = Math.ceil((maxQualitySize.quality + minQualitySize.quality) / 2);if (maxQualitySize.quality - minQualitySize.quality < 2) {if (!minQualitySize.size && quality) {quality = minQualitySize.quality;} else if (!minQualitySize.size && !quality) {compressFinish = true;invalidDesc = '压缩失败,无法压缩到指定大小';console.log(`压缩完成,总共压缩了${count}次`);} else if (minQualitySize.size > maxSize) {compressFinish = true;invalidDesc = '压缩失败,无法压缩到指定大小';console.log(`压缩完成,总共压缩了${count}次`);} else {console.log(`压缩完成,总共压缩了${count}次`);compressFinish = true;quality = minQualitySize.quality;}}}if (invalidDesc) {// 压缩失败,则返回原始图片的信息console.log(`压缩失败,无法压缩到指定大小:${maxSize}KB`);reject({ msg: invalidDesc, compressBase64: base64, compressFile: originfile });return;}compressBlob = await canvastoFile(canvas, 'image/jpeg', quality / 100);const compressSize = compressBlob.size / 1024;console.log(`最后一次压缩(即第${count + 1}次),quality为:${quality},大小:${compressSize}`);const compressedBase64 = await fileToDataURL(compressBlob);const compressedFile = new File([compressBlob], originfile.name, { type: 'image/jpeg' });resolve({ compressFile: compressedFile, compressBase64: compressedBase64 });
});export default compress;

3.less

.uploadContainer{display: grid;grid-template-columns: repeat(auto-fill,minmax(75px, 1fr));grid-row-gap: .10rem;grid-column-gap: .08rem;.gridItem{position: relative;width: 100%;height: 0;padding-top: 100%;.inputContainer{position: absolute;left: 0;top: 0;width: 100%;height: 100%;display: flex;align-items: center;justify-content: center;border-radius:8px;border:1px solid rgba(217,217,217,1);.fileInput{display: none;}.uploadIcon{font-size: 30px;color: lightgrey;}}img{position: absolute;left: 0;top: 0;width: 100%;height: 100%;border-radius:8px;}.delete{position: absolute;top: -9px;right: -9px;width: 18px;height: 18px;background: red;color: white;border-radius: 50%;display: flex;align-items: center;justify-content: center;font-size: 20px;}.filter{filter: blur(1px);}.progressContainer{position: absolute;width: 80%;left: 50%;top: 50%;transform: translate(-50%, -50%);font-size: 10px;.progress{width: 100%;height: 4px;border-radius: 3px;border: 1px solid rgba(0,0,0,0.1);}.progressHighlight{height: 100%;width: 100%;animation: progress 3s cubic-bezier(0.25,0.1,0.25,1) infinite;background: orange;border-radius: 3px;}}}
}@keyframes progress
{0%   {width: 0}to  {width: 100%}
}

js压缩图片到指定大小相关推荐

  1. Thumbnails压缩图片到指定大小

    网上看了很多demo,很多都是照搬别人的代码,不管有没有问题,有的甚至递归不关流,还有的递归疯狂往自己磁盘写文件,递归一次写一次,我自己把网上的demo整理改了下发出来. /** * @Descrip ...

  2. 如何压缩图片到500KB?怎么压缩图片到指定大小?

    如何压缩图片到500KB?这就需要用到压缩啦的图片压缩指定大小工具,只需三步就可以轻松压缩图片大小kb,具体操作步骤如下: 1.使用浏览器打开压缩啦,选择图片压缩指定大小功能. 2.上传需要压缩的图片 ...

  3. JPG图片怎么压缩到30k内?如何压缩图片到指定大小?

    现在各大网站对上传使用的图片都有大小的限制,有时我们在上传图片的时候会遇到图片要求要在30k以内的情况,在遇到这种情况的时候大家就可以使用在线照片处理器 (https://www.yasuotu.co ...

  4. java 压缩图片至指定大小

    需求: 压缩图片至300k以下 一开始没有思路在网上搜,发现google有个插件叫Thumbnails,然后看到了这篇文章: https://blog.csdn.net/u010355502/arti ...

  5. python——实现压缩图片至指定大小

    一.问题描述 遇到了一次那种要求上传图片,图片大小要在1M以内的,一开始尝试用python的cv2来resize实现,后来没成功,百度发现可以用以下方法实现. 二.代码如下 import os fro ...

  6. python 压缩图片为指定大小

    转自:scipy笔记-scipy.misc.imresize用法(方便训练图像数据) from skimage.transform import resize # 压缩图片 def predict_m ...

  7. Android学习之压缩图片到指定大小

    关于图片压缩,是为了上传服务器时有些地方有大小限制,因此,这里我总结了两种方法,个人感觉方法一比较准确一点. 方法一: * 图片压缩方法一* * 计算 bitmap大小,如果超过64kb,则进行压缩* ...

  8. 快速批量压缩照片到指定大小工具,照片图片批量压缩实现方法,批量压缩工具

    在工作中,会遇到在某些系统要上传照片,但是对于上传的照片大小有限制,比如限制大小不能超过1MB等,而外业拍摄的照片往往会超过限制的大小,那么这时就需要对照片进行压缩.但是外业照片数据巨大,人工逐张操作 ...

  9. 十分钟教会你原生JS压缩图片,极其精简版

    十分钟教会你原生JS压缩图片,极其精简版 原文链接:https://blog.csdn.net/yasha97/article/details/83629510 (一)实现思路 先通过input标签获 ...

  10. 怎么压缩图片的体积大小,4款软件分享

    怎么压缩图片的体积大小?因为在日常生活和工作中,我们常常会遇到需要压缩图片大小的情况.图片的大小是由像素点数量和每个像素的颜色深度共同决定的,一般来说,像素点数量越多,每个像素的颜色深度越高,图片的清 ...

最新文章

  1. Datawhale组队学习 Task02:顺序表和链表(2天)
  2. 深度学习不可信?大脑建模先驱有话说
  3. 【原创】【专栏】《Linux设备驱动程序》--- LDD3源码目录结构和源码分析经典链接
  4. Kubernetes 集群升级指南:从理论到实践
  5. 常用计算机类型包括个人计算机,网络教育统考《计算机应用基础》多媒体技术模拟题(二)...
  6. java中如何将非整数保留到小数点后指定的位数
  7. 100个高低压配电知识!网络弱电必备知识~
  8. 写出表格的结构html,一个面试题,根据json结构生成html表格
  9. 最近见了几位东半球的顶级技术大拿
  10. java多线程命名,命名线程和当前线程
  11. 星光计划计算机,计算机网络技术专业星光计划选拔赛圆满举行
  12. mockito简单教程
  13. 电线的一些小知识学习一下
  14. 5+API实现微信分享功能
  15. [python]---药品数据分析及预测(包括数据集,源码,报告)
  16. ZFM_RFC_FIDOC-创建财务凭证-BAPI_ACC_DOCUMENT_CHECK/BAPI_ACC_DOCUMENT_POST/POSTING_INTERFACE_DOCUMENT
  17. Tomocat:安装完成,显示HTTP Status 404
  18. 微信小程序微信授权登录的昵称头像授权数量上限,如何删除
  19. Appium+Python MAC安装Android夜神模拟器(二)
  20. SQL中去除重复数据的几种方法,我一次性都告诉你​

热门文章

  1. 股指期货日内平仓手续费高,锁仓可以解决吗
  2. 员工转正答辩官提问_转正答辩(交付经理)--王总提问6.19
  3. 【迅为6818开发板资料】安卓7.1 系统编译手册
  4. offer收割者!Alibaba内部独家MySQL优化宝典横空出世,再也不用担心被面试官拦路了
  5. python培训课程-python培训课程
  6. linux 删除中文文件夹,解析Linux文件夹文件创建、删除
  7. xp计算机组策略怎么打开,WinXP系统打开组策略的命令是什么?
  8. php加cnzz,Vue单页面中添加cnzz统计
  9. 转型只争朝夕!又一火电企业成立新能源公司
  10. python正则判断邮箱_Python实现正则表达式匹配任意的邮箱方法