一个简单的-命令行-图片压缩工具

一个简单的 命令行 图片压缩工具https://tinyjpg.com/ 是一个图片压缩网站, 在进行图片压缩的同时非常好的保存了图片的质量. 相信做前端的同学很多都用到过. 偶然一次发现该网站有提供Developer API , 点开一看还发现有很多已经封装好的第三方package. 但这些package都是提供编程时使用的, 很多时候我们需要的是快速压缩一两张图片, 而不是编写脚本去执行代码. 所以为了方面命令行使用, 我将其封装成了一个 global 的npm包. 安装后即可全局使用. 使用方式安装该命令行工具 npm install -g @ssthouse/img-compress设置 API key (注: API key 需要在 tiniyjpg网站 进行申请, 提供用户名和邮箱即可得到一个 500次/ 每月 的key) img-compress init -key your_api_key如果你的terminal网络需要使用代理的话, 需要进行设置: img-compress proxy http://example.com开始压缩图片 img-process img_file命令执行成功后会在原图的同一目录看到压缩过的图片文件. 文件名以 __compress.文件后缀 结尾. 如: test.png => test_compress.png 查看使用说明不带参数的执行该工具就可以看到使用说明, 如有遇到问题可以在这里提issue: @ssthouse/img-compress 最后如果你对我的文章感兴趣, 这里有我的一些 数据可视化, D3.js 方面的文章, 欢迎 fork && star: https://github.com/ssthouse/s... ...

June 1, 2019 · 1 min · jiezi

帮谷歌推广Webp图片格式之:Webp的格式转换

参考谷歌官网:Webp: A new image format for the WebWebp是Google强推的新一代网络图片格式,特点就是:高质量压缩。能压缩多少呢?5MB的原图,不降低效果,转换成webp格式后大小是几百KB。100KB的图,转换后是9KB。虽然目前所有主流浏览器都支持这种图片格式,但不幸的是所有主流系统如Mac、Win等都还没有默认支持打开它的程序,更无法显示它的预览、缩略图。如果想查看,最简单的方法是把*.webp文件的打开方式设定为Chrome等浏览器,双击打开在浏览器中查看。还有很多时候我们需要对这种文件进行转换。Google提供了一组工具集合,叫libwebp,其中包括各种webp相关转换的命令:cwebp – 将其它图片转为webp格式图片 (不包括GIF)dwebp – 将webp格式图片转为其它格式图片vwebp – webp图片浏览器webpmux – WebP muxing toolgif2webp – 将GIF转换为webp图片下载安装参考官网:Downloading and Installing WebPUbuntu安装libweb库:$ sudo apt-get install webpMac安装libwebp库:$ brew install webp注意:Homebrew安装的webp并不包括上面所有的工具,而只有cwebp和dwebp。如果我们想要所有的工具,有两种方法:到官网找到自己OS对应版本的二进制包,直接运行使用自己编译最简单就是到官网下载列表里找到自己的OS对应版本的二进制包,下载下来解压缩直接使用。官方下载列表:https://storage.googleapis.co…比如我的系统是Mac 10.12,那么就找到libwebp-0.6.0-mac-10.12.tar.gz这个压缩包下载:cd /tmpwget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-0.6.0-mac-10.12.tar.gztar xvzf libwebp-*.tar.gzcd libweb-*然后在~/.zshrc或~/.bash_profile中的PATH环境变量中加入刚才二进制文件包中的bin目录,或者直接设置alias,即可开始像别的命令开始用了。如果没有自己所用系统的二进制包,那么就只能自己编译了。每种平台的编译方法不一样,需要按照官网方法一步一步安装。编译方法参考官方:Compiling the Utilities将各种图片转换为Webp格式参考:https://developers.google.com…目前输入格式支持:png, jpg$ cwebp INPUT.png -o OUTPUT.webp将Webp图片转换为其它格式图片参考:https://developers.google.com…$ dwebp INPUT.webp -o OUTPUT.png将GIF转换为Webp格式参考:https://developers.google.com…$ gif2webp INPUT.gif -o OUTPUT.webp浏览webp图片这个命令不是在命令行终端里浏览图片,而是在桌面上弹出一个GUI窗口显示图片,所以需要依赖本地电脑的GUI桌面。$ vwebp INPUT.webp

February 17, 2019 · 1 min · jiezi

图片压缩工具推荐

为什么要压缩图片如果你是web开发者:减小网页、小程序的体积,提升加载速度如果你是App开发者:减小App的打包体积,提升下载速度如果你是新媒体从业者:文章体积更小,加快打开速度如果你是职场用户:邮件附件超限、微信发送太慢,你总会遇到经过压缩图片还清晰吗图片压缩分为有损压缩和无损压缩无损压缩:保证清晰度,但是压缩率很低有损压缩:压缩率可以很高,清晰度视情况而定主流的有损压缩工具可以在压缩率、清晰度之间保持一个平衡点不牺牲视觉体验,又能尽可能压缩文件体积工具推荐docsmall在线图片压缩我们开发的图片压缩网站,上传速度极快(使用的阿里云OSS),压缩率也很好,可以压缩png、jpg,GIF压缩即将上线除了图片的压缩外,还支持PDF的压缩、合并、分割tinypng国外的网站,上传速度总会有波动,但是针对小图还好ps:网站上的熊猫很呆萌智图腾讯团队出品,有windows、Mac客户端可以使用,同时支持自定义压缩质量

February 12, 2019 · 1 min · jiezi

用element的upload组件实现多图片上传和压缩

我用vuex做状态管理,七牛云做图床。项目地址:多图片上传组件效果展示项目执行流程首先,让我们来分析一下实现多图片上传的流程:前端向后端请求用来上传图片至服务器的token后端为每张要上传的图片生成一个图片名,并用这个图片名生成token后端将图片名和token返回给前端前端拿到token以后,将图片上传至服务器上传成功以后,前端将图片名发给后端后端将图片名存入数据库项目实现过程1.我们要利用element-ui的Upload组件布置界面://upload.vue<el-upload :action= domain ref=“upload” accept=‘image/jpeg,image/gif,image/png’ :auto-upload=“false” :http-request=“upqiniu” :limit=“limit” :multiple=“multiple” list-type=“picture-card” :before-upload=“beforeUpload” :on-preview=“handlePictureCardPreview” :on-change=“handldChange” :on-remove=“handleRemove”> <i class=“el-icon-plus”></i></el-upload><el-dialog :visible.sync=“dialogVisible”> <img width=“100%” :src=“dialogImageUrl” alt=""></el-dialog>domain 指的是我们的上传地址,upqiniu 是我们自定义的上传方法,beforeUpload 是图片上传前执行的方法。关于该组件的其他用法可以在element的官方文档查阅:Upload 上传2.对图片进行压缩// upload.vueimgQuality: 0.5, //压缩图片的质量dataURItoBlob(dataURI, type) { var binary = atob(dataURI.split(’,’)[1]); var array = []; for(var i = 0; i < binary.length; i++) { array.push(binary.charCodeAt(i)); } return new Blob([new Uint8Array(array)], {type: type});},beforeUpload(param) { //对图片进行压缩 const imgSize = param.size / 1024 / 1024 if(imgSize > 1) { const _this = this return new Promise(resolve => { const reader = new FileReader() const image = new Image() image.onload = (imageEvent) => { const canvas = document.createElement(‘canvas’); const context = canvas.getContext(‘2d’); const width = image.width * _this.imgQuality const height = image.height * _this.imgQuality canvas.width = width; canvas.height = height; context.clearRect(0, 0, width, height); context.drawImage(image, 0, 0, width, height); const dataUrl = canvas.toDataURL(param.type); const blobData = this.dataURItoBlob(dataUrl, param.type); resolve(blobData) } reader.onload = (e => { image.src = e.target.result; }); reader.readAsDataURL(param); }) }}压缩图片实现起来比较简单。就是在beforeUpload()方法里面return一个Promise,Promise里面我们把图片的长度和宽度按比例进行缩小,并把图片画到canvas上,然后把canvas转成一个blod对象。3.前端向后端请求上传token。//upload.vueupqiniu(param) { let filetype = ’’ if (param.file.type === ‘image/png’) { filetype = ‘png’ } else { filetype = ‘jpg’ } const formdata = { filetype: filetype, param: param } this.actionGetUploadToken(formdata) }// vuex/action.jsactionGetUploadToken({commit}, obj) { const msg = { filetype: obj.filetype } usersApi.getImgUploadToken(msg).then((response) => { if(response.stateCode === 200) { commit(‘uploadImg’, {’token’: response.token, ‘key’: response.key, ‘param’: obj.param}) } }, (error) => { console.log(获取图片上传凭证错误:${error}) commit(‘uploadImgError’) })},4.后端生成上传token,并发给前端,我用Python实现。filetype = data.get(‘filetype’)# 构建鉴权对象q = Auth(configs.get(‘qiniu’).get(‘AK’), configs.get(‘qiniu’).get(‘SK’))# 生成图片名salt = ‘’.join(random.sample(string.ascii_letters + string.digits, 8))key = salt + ‘’ + str(int(time.time())) + ‘.’ + filetype# 生成上传 Token,可以指定过期时间等token = q.upload_token(configs.get(‘qiniu’).get(‘bucket_name’), key, 3600)return Response({“stateCode”: 200, “token”: token, “key”: key}, 200)5.前端接收token,开始向服务器上传图片// vuex/state.jsimgName: [], //图片名数组// vuex/mutations.jsuploadImg(state, msg) { const config = { useCdnDomain: true, region: qiniu.region.z2 } var putExtra = { fname: msg.param.file.name, params: {}, mimeType: [“image/png”, “image/jpeg”, “image/gif”] }; var observer = { next(res){ }, error(err){ console.log(图片上传错误信息:${err.message}) }, complete(res){ console.log(图片上传成功:${res.key}) state.imgName.push(res.key) } } var observable = qiniu.upload(msg.param.file, msg.key, msg.token, putExtra, config) //上传开始 var subscription = observable.subscribe(observer)},6.上传成功以后,将图片名存入数据库// 用到upload.vue的界面this.imgsList = this.imgName.map(key => http://${this.qiniuaddr}/${key})switch(this.imgsList.length) { case 4: this.img4 = this.imgsList[3] case 3: this.img3 = this.imgsList[2] case 2: this.img2 = this.imgsList[1] case 1: this.img1 = this.imgsList[0]}let obj = { goods_img1: this.img1, goods_img2:this.img2, goods_img3:this.img3, goods_img4:this.img4}//将信息发送给后端this.actionPublish(obj) ...

February 11, 2019 · 2 min · jiezi

利用 canvas 压缩图片

利用 canvas 压缩图片前言在一个移动端的项目中,图片上传是一个比较常用的功能。但是,目前手机的随便拍的照片一张都要好几 M , 直接上传的话特别耗费流量,而且所需时间也比较长。所以需要前端在上传之前先对图片进行压缩。原理要使用 js 实现图片压缩效果, 原理其实很简单,主要是:利用 canvas 的 drawImage 将目标图片画到画布上利用画布调整绘制尺寸,以及导出的 quality ,确定压缩的程度利用 canvas的 toDataURL 或者 toBlob 可以将画布上的内容导出成 base64 格式的数据。注意点IOS 下会出现图片翻转的问题这个需要 import EXIF from ’exif-js’;来获取到手机的方向,然后对 canvas 的宽高进行处理压缩到特定大小let imgDataLength = dataUrl.length; 获取到数据后,判断压缩后的图片大小是否满足需求,否则就降低尺寸以及质量,再次压缩quality 对 png 等无效,所以导出格式统一为 jpeg ,透明背景填充为白色// 填充白色背景ctx.fillStyle = fillBgColor;ctx.fillRect(0, 0, size.w, size.h);具体源码/** * 文件读取并通过canvas压缩转成base64 * @param files * @param callback ///EXIF js 可以读取图片的元信息 https://github.com/exif-js/exif-jsimport EXIF from ’exif-js’;// 压缩图片时 质量减少的值const COMPRESS_QUALITY_STEP = 0.03;const COMPRESS_QUALITY_STEP_BIG = 0.06;// 压缩图片时,图片尺寸缩放的比例,eg:0.9, 等比例缩放为0.9const COMPRESS_SIZE_RATE = 0.9;let defaultOptions = { removeBase64Header: true, // 压缩后允许的最大值,默认:300kb maxSize: 200 * 1024, fillBgColor: ‘#ffffff’};/* * 将待上传文件列表压缩并转换base64 * !!!! 注意 : 图片会默认被转为 jpeg , 透明底会加白色背景 * files : 文件列表 ,必须是数组 * callback : 回调,每个文件压缩成功后都会回调, * options :配置 * options.removeBase64Header : 是否需要删除 ‘data:image/jpeg;base64,‘这段前缀,默认true * @return { base64Data: ‘’,fileType: ’’ }, //fileType强制改为jpeg /export function imageListConvert(files, callback, options = {}) { if (!files.length) { console.warn(‘files is null’); return; } options = { …defaultOptions, …options }; // 获取图片方向--iOS拍照下有值 EXIF.getData(files[0], function() { let orientation = EXIF.getTag(this, ‘Orientation’); for (let i = 0, len = files.length; i < len; i++) { let file = files[i]; let fileType = getFileType(file.name); //强制改为jpeg fileType = ‘jpeg’; let reader = new FileReader(); reader.onload = (function() { return function(e) { let image = new Image(); image.onload = function() { let data = convertImage( image, orientation, fileType, options.maxSize, options.fillBgColor ); if (options.removeBase64Header) { data = removeBase64Header(data); } callback({ base64Data: data, fileType: fileType }); }; image.src = e.target.result; }; })(file); reader.readAsDataURL(file); } });}/* * 将 image 对象 画入画布并导出base64数据 /export function convertImage( image, orientation, fileType = ‘jpeg’, maxSize = 200 * 1024, fillBgColor = ‘#ffffff’) { let maxWidth = 1280, maxHeight = 1280, cvs = document.createElement(‘canvas’), w = image.width, h = image.height, quality = 0.9; /* * 这里用于计算画布的宽高 / if (w > 0 && h > 0) { if (w / h >= maxWidth / maxHeight) { if (w > maxWidth) { h = (h * maxWidth) / w; w = maxWidth; } } else { if (h > maxHeight) { w = (w * maxHeight) / h; h = maxHeight; } } } let ctx = cvs.getContext(‘2d’); let size = prepareCanvas(cvs, ctx, w, h, orientation); // 填充白色背景 ctx.fillStyle = fillBgColor; ctx.fillRect(0, 0, size.w, size.h); //将图片绘制到Canvas上,从原点0,0绘制到w,h ctx.drawImage(image, 0, 0, size.w, size.h); let dataUrl = cvs.toDataURL(image/${fileType}, quality); //当图片大小 > maxSize 时,循环压缩,并且循环不超过5次 let count = 0; while (dataUrl.length > maxSize && count < 10) { let imgDataLength = dataUrl.length; let isDoubleSize = imgDataLength / maxSize > 2; // 质量一次下降 quality -= isDoubleSize ? COMPRESS_QUALITY_STEP_BIG : COMPRESS_QUALITY_STEP; quality = parseFloat(quality.toFixed(2)); // 图片还太大的情况下,继续压缩 。 按比例缩放尺寸 let scaleStrength = COMPRESS_SIZE_RATE; w = w * scaleStrength; h = h * scaleStrength; size = prepareCanvas(cvs, ctx, w, h, orientation); //将图片绘制到Canvas上,从原点0,0绘制到w,h ctx.drawImage(image, 0, 0, size.w, size.h); console.log(imgDataLength:${imgDataLength} , maxSize --&gt; ${maxSize}); console.log(size.w:${size.w}, size.h:${size.h}, quality:${quality}); dataUrl = cvs.toDataURL(image/jpeg, quality); count++; } console.log(imgDataLength:${dataUrl.length} , maxSize --&gt; ${maxSize}); console.log(size.w:${size.w}, size.h:${size.h}, quality:${quality}); cvs = ctx = null; return dataUrl;}/* * 准备画布 * cvs 画布 * ctx 上下文 * w : 想要画的宽度 * h : 想要画的高度 * orientation : 屏幕方向 /function prepareCanvas(cvs, ctx, w, h, orientation) { cvs.width = w; cvs.height = h; //判断图片方向,重置canvas大小,确定旋转角度,iphone默认的是home键在右方的横屏拍摄方式 let degree = 0; switch (orientation) { case 3: //iphone横屏拍摄,此时home键在左侧 degree = 180; w = -w; h = -h; break; case 6: //iphone竖屏拍摄,此时home键在下方(正常拿手机的方向) cvs.width = h; cvs.height = w; degree = 90; // w = w; h = -h; break; case 8: //iphone竖屏拍摄,此时home键在上方 cvs.width = h; cvs.height = w; degree = 270; w = -w; // h = h; break; } // console.log(orientation --&gt; ${orientation} , degree --&gt; ${degree}); // console.log(w --&gt; ${w} , h --&gt; ${h}); //使用canvas旋转校正 ctx.rotate((degree * Math.PI) / 180); return { w, h };}/* * 截取 ‘data:image/jpeg;base64,’, * 截取到第一个逗号 */export function removeBase64Header(content) { if (content.substr(0, 10) === ‘data:image’) { let splitIndex = content.indexOf(’,’); return content.substring(splitIndex + 1); } return content;}export function getFileType(fileName = ‘’) { return fileName.substring(fileName.lastIndexOf(’.’) + 1);}export function checkAccept( file, accept = ‘image/jpeg,image/jpg,image/png,image/gif’) { return accept.toLowerCase().indexOf(file.type.toLowerCase()) !== -1;}相关链接个人博客代码片段 ...

December 20, 2018 · 3 min · jiezi