大家好,欢迎来到IT知识分享网。
作者:Chess
转发链接:https://mp.weixin..com/s/aRyEzYcJ7CvOaN6ll5C10Q
前言
前端er,又称为切图仔,平时经常需要用PSD导出PNG或JPG,但是导出来的的图片一般比较大,往往需要用一些其他工具压缩后再发布到生产环境。
以前常用的做法是,使用image-webpack-loader,在webpack打包项目时自动压缩图片。但是这loader是基于imagemin的,他的压缩导致画质损失比较严重,而且压缩率也不是很高。经常出现一种情况就是,在本地开发的时候,图片明明很清晰的,但是一旦发布到生产环境,因为经过了img-webapck-loader压缩,导致画质严重损失。也因为这个原因,美术小姐姐在验收项目的时候,常常会把我叫过去,说:哎呀,我的PSD明明是很清晰的呀,为什么你放到网站上去就那么模糊了呢。我说因为压缩过了呀。然后她说这个压缩工具不给力呀,就推荐了一个在线压缩工具给我:TinyPNG在线地址。
初体验tinypng真是把我惊呆了,一般的PNG压缩率竟然可以高达50%-70%,并且肉眼看不出来任何的画质损失!
TinyPNG的原理是将PNG24位真彩色图片压缩成PNG8位索引图片,从而做到基本不损失画质和观感。至于具体算法怎么实现的也没有深入研究守,有兴趣的可以自行查阅相关资料。
tinypg除了可以通过网页端上传图片,也提供了api的方式,所以可以使用node或其他脚本语言进行上传的,可以参考这里:https://tinify.cn/developers/reference。
然而,这么好用的压缩工具有一个很明显缺陷的,就是每天只能正常压缩20张图片。超过20张之后,就会经常出来Too many files uploaded at once。如下红色的部分:
当然,这是针对免费版用户而言的。如果想要避免这个限制,你需要花25美金买一年的会员。当然,我们程序员大多数都是很吝啬的,就连到外面吃饭要不要加个卤蛋都要纠结半个小时的,更别说给25美金他了。
但是,程序员的特点是,都爱折腾!所以我决定要尝试着绕过这20张图片的上传限制。一般来说,这种免登录就可试用的系统,都是通过用户IP来限制用户的操作次数的,所以就决定从修改IP的方式来绕开这个限制。
最简单的作法是使用动态的代理IP,比如这里https://ip.ihuan.me/就有一些免费代理IP,但是,这些IP都是国外的,很慢的,我们的目的是要上传并压缩图片,使用这么慢的代理显然是不可行的。
所以就只能寻找其他的办法。就在陷入迷茫的时候,突然想到了一个事实:目前的web架构大多数都是通过nginx作为反向代理的,如下:
从上图可以看出,客户端并不是直接请求应用服务的,而是通过统一接入层(往往是nginx)来转发请求的。那么问题来,应用服务是如何获取到客户端的IP的呢?
玩过nginx的同学应该都知道,这个通用的解决方案就是X-Forwarded-For请求头。
简单的来说,就是所有的反向代理都实现一个统一的约定,在转发请求给下游服务之前,把请求代理的IP地址写入到X-Forwarded-For头中,形成了一个IP地址列:
X-Forwarded-For: client, proxy1, proxy2
这个方案虽然不是正式的HTTP协议,但已经成为了一个事实标准,基本上所有的反向代理服务都实现了这个功能,以确保下游的服务可以感知到经过的反向代理,并从中获取到用户的IP地址。
好了,既然应用服务是通过X-Forwarded-For头部来获取客户端IP的,那么我们能不能在客户端伪造这个头部,每次上传图片的时候都设置一个随机的IP呢?所以就决定尝试一下。结果发现,该方法确实可行!竟然可以欺骗tinypng的服务器,从而绕开了上传次数的限制。
完整代码如下:
const fs = require("fs"); const path = require("path"); const https = require("https"); const { URL } = require("url"); const cwd = process.cwd(); const root = cwd; const exts = [".jpg", ".png"]; const max = ; // 5MB == . const options = { method: "POST", hostname: "tinypng.com", path: "/web/shrink", headers: { rejectUnauthorized: false, "Postman-Token": Date.now(), "Cache-Control": "no-cache", "Content-Type": "application/x-www-form-urlencoded", "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" } }; fileList(root); // 生成随机IP,赋值给X-Forwarded-For function getRandomIP() { return Array.from(Array(4)).map(() => parseInt(Math.random() * 255)).join("."); } // 获取文件列表 function fileList(folder) { fs.readdir(folder, (err, files) => { if (err) console.error(err); files.forEach(file => { fileFilter(path.join(folder, file)); }); }); } // 过滤文件格式,返回所有jpg,png图片 function fileFilter(file) { fs.stat(file, (err, stats) => { if (err) return console.error(err); if ( // 必须是文件,小于5MB,后缀 jpg||png stats.size <= max && stats.isFile() && exts.includes(path.extname(file)) ) { // 通过 X-Forwarded-For 头部伪造客户端IP options.headers["X-Forwarded-For"] = getRandomIP(); fileUpload(file); // console.log('可以压缩:' + file); } // if (stats.isDirectory()) fileList(file + '/'); }); } // 异步API,压缩图片 // {"error":"Bad request","message":"Request is invalid"} // {"input": { "size": 887, "type": "image/png" },"output": { "size": 785, "type": "image/png", "width": 81, "height": 81, "ratio": 0.885, "url": "https://tinypng.com/web/output/7aztz90nq5p9545zch8gjzqg5ubdatd6" }} function fileUpload(img) { var req = https.request(options, function(res) { res.on("data", buf => { const obj = JSON.parse(buf.toString()); if (obj.error) { console.log(`[${img}]:压缩失败!报错:${obj.message}`); } else { fileUpdate(img, obj); } }); }); req.write(fs.readFileSync(img), "binary"); req.on("error", e => { console.error(e); }); req.end(); } // 该方法被循环调用,请求图片数据 function fileUpdate(imgpath, obj) { const outputDir = path.join(cwd, "output"); imgpath = path.join(cwd, "output", imgpath.replace(cwd, "")); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir); } const options = new URL(obj.output.url); const req = https.request(options, res => { let body = ""; res.setEncoding("binary"); res.on("data", function(data) { body += data; }); res.on("end", function() { fs.writeFile(imgpath, body, "binary", err => { if (err) return console.error(err); console.log( `[${imgpath}] \n 压缩成功,原始大小-${obj.input.size},压缩大小-${ obj.output.size },优化比例-${obj.output.ratio}` ); }); }); }); req.on("error", e => { console.error(e); }); req.end(); }
这段代码是参考了nodejs全自动使用Tinypng(免费版,无需任何配置)压缩图片,只是简单的加上了动态的IP头,从而实现了不受上传次数限制。
核心的代码在这里:
// 生成随机IP,赋值给X-Forwarded-For function getRandomIP() { return Array.from(Array(4)).map(() => parseInt(Math.random() * 255)).join("."); } // .... options.headers["X-Forwarded-For"] = getRandomIP(); // ....
代码上传到了github,有兴趣的可以点击下面链接查看并star关注喔:
super-tinypng
同时,也发布到了npm上面。只需要全局安装:
npm i -g super-tinypng
然后在你想要压缩图片的目录里面运行super-tinypng就能自动压缩图片了,并且不会有数量限制!
作者:Chess
转发链接:https://mp.weixin..com/s/aRyEzYcJ7CvOaN6ll5C10Q
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/88044.html