这里吐槽一下tinypng 的接口写的真的烂。。在查询key的合法性的 validate 函数只接受报错的回调,但是成功却没有任何动作。我真是服了,之前是做延时来判断用户的key的合法性,最后实在是受不了这个bug一样的写法了,决定用Object.defineProperty来监听它的使用次数的变化。如果它的setter被调用则说明它是一个合法的key了
5、小结
在这里,我想跟大家说,如果你做了一个你觉得很酷的东西,也想给更多的人去使用,来让它变得更好,选择发布在NPM上面就是一个非常好的途径,看了上面的内容你会发现分享其实真的不难,你也有机会让世界看到属于你的风采!
如果大家觉得我有哪里写错了,写得不好,有其它什么建议(夸奖),非常欢迎大家补充。希望能让大家交流意见,相互学习,一起进步! 我是一名 19 的应届新人,以上就是今天的分享,新手上路中,后续不定期周更(或者是月更哈哈),我会努力让自己变得更优秀、写出更好的文章,文章中有不对之处,烦请各位大神斧正。如果你觉得这篇文章对你有所帮助,请记得点赞或者品论留言哦~。
6、写在最后
欢迎大家提issue或者建议!地址在这:
https://github.com/Croc-ye/tinyhere
https://www.npmjs.com/package/tinyhere
最后贴上部分代码,内容过长,可以跳过哦
bin/tinyhere
#!/usr/bin/env node const commander = require('commander'); const {init, addKey, deleteKey, emptyKey, list, compress} = require('../libs/subCommand.js'); const {getKeys} = require('../libs/util.js'); // 主命令 commander .version(require('../package').version, '-v, --version') .usage('[options]') .option('-p, --path <newPath>', '压缩后的图片存放到指定路径(使用相对路径)') .option('-a, --add <key>', '添加api-key') .option('--delete <key>', '删除指定api-key') .option('-l, --list', '显示已储存的api-key') .option('--empty', '清空已储存的api-key') // 子命令 commander .command('deep') .description('把该目录内的所有图片(含子目录)的图片都进行压缩') .action(()=> { // deepCompress(); console.log('尚未完成,敬请期待'); }) commander.parse(process.argv); // 选择入口 if (commander.path) { // 把图片存放到其他路径 compress(commander.path); } else if (commander.add) { // 添加api-key addKey(commander.add); } else if (commander.delete) { // 删除api-key deleteKey(commander.delete); } else if (commander.list) { // 显示api-key list(); } else if (commander.empty) { // 清空api-key emptyKey(); } else { // 主命令 if (typeof commander.args[0] === 'object') { // 子命令 return; } if (commander.args.length !== 0) { console.log('未知命令'); return; } if (getKeys().length === 0) { console.log('请初始化你的api-key') init(); } else { compress(); } };
libs/compress.js
const tinify = require('tinify'); const fs = require("fs"); const path = require('path'); const imageinfo = require('imageinfo'); const inquirer = require('inquirer'); const {checkApiKey, getKeys} = require('./util'); // 对当前目录内的图片进行压缩 const compress = (newPath = '')=> { const imageList = readDir(); if (imageList.length === 0) { console.log('当前目录内无可用于压缩的图片'); return; } newPath = path.join(process.cwd(), newPath); mkDir(newPath); findValidateKey(imageList.length); console.log('===========开始压缩========='); if (newPath !== process.cwd()) { console.log('压缩到: ' + newPath.replace(/\./g, '')); } compressArray(imageList, newPath); }; // 生成目录路径 const mkDir = (filePath)=> { if (filePath && dirExists(filePath) === false) { fs.mkdirSync(filePath); } } // 判断目录是否存在 const dirExists = (filePath)=> { let res = false; try { res = fs.existsSync(filePath); } catch (error) { console.log('非法路径'); process.exit(); } return res; }; /** * 检查api-key剩余次数是否大于500 * @param {*} count 本次需要压缩的图片数目 */ const checkCompressionCount = (count = 0)=> { return (500 - tinify.compressionCount - count) >> 0; } /** * 找到可用的api-key * @param {*} imageLength 本次需要压缩的图片数目 */ const findValidateKey = async imageLength=> { // bug高发处 const keys = getKeys(); for (let i = 0; i < keys.length; i++) { await checkApiKey(keys[i]); res = checkCompressionCount(imageLength); if (res) return; } console.log('已存储的所有api-key都超出了本月500张限制,如果要继续使用请添加新的api-key'); process.exit(); } // 获取当前目录的所有png/jpg文件 const readDir = ()=> { const filePath = process.cwd() const arr = fs.readdirSync(filePath).filter(item=> { // 这里应该根据二进制流及文件头获取文件类型mime-type,然后读取文件二进制的头信息,获取其真实的文件类型,对与通过后缀名获得的文件类型进行比较。 if (/(\.png|\.jpg|\.jpeg)$/.test(item)) { // 求不要出现奇奇怪怪的文件名。。 const fileInfo = fs.readFileSync(item); const info = imageinfo(fileInfo); return /png|jpg|jpeg/.test(info.mimeType); } return false; }); return arr; }; /** * 对数组内的图片名进行压缩 * @param {*} imageList 存放图片名的数组 * @param {*} newPath 压缩后的图片的存放地址 */ const compressArray = (imageList, newPath)=> { const failList = []; imageList.forEach(item=> { compressImg(item, imageList.length, failList, newPath); }); } /** * 压缩给定名称的图片 * @param {*} name 文件名 * @param {*} fullLen 全部文件数量 * @param {*} failsList 压缩失败的数组 * @param {*} filePath 用来存放的新地址 */ const compressImg = (name, fullLen, failsList, filePath)=> { fs.readFile(name, function(err, sourceData) { if (err) throw err; tinify.fromBuffer(sourceData).toBuffer(function(err, resultData) { if (err) throw err; filePath = path.join(filePath, name); const writerStream = fs.createWriteStream(filePath); // 标记文件末尾 writerStream.write(resultData,'binary'); writerStream.end(); // 处理流事件 --> data, end, and error writerStream.on('finish', function() { failsList.push(null); record(name, true, failsList.length, fullLen); if (failsList.length === fullLen) { finishcb(failsList, filePath); } }); writerStream.on('error', function(err){ failsList.push(name); record(name, false, failsList.length, fullLen); if (failsList.length === fullLen) { finishcb(failsList, filePath); } }); }); }); } // 生成日志 const record = (name, success = true, currNum, fullLen)=> { const status = success ? '完成' : '失败'; console.log(`${name} 压缩${status}。 ${currNum}/${fullLen}`); } /** * 完成调用的回调 * @param {*} failList 存储压缩失败图片名的数组 * @param {*} filePath 用来存放的新地址 */ const finishcb = (failList, filePath)=> { const rest = 500 - tinify.compressionCount; console.log('本月剩余次数:' + rest); const fails = failList.filter(item=> item !== null); if (fails.length > 0) { // 存在压缩失败的项目(展示失败的项目名),询问是否把压缩失败的继续压缩 y/n // 选择否之后,询问是否生成错误日志 inquirer.prompt({ type: 'confirm', name: 'compressAgain', message: '存在压缩失败的图片,是否将失败的图片继续压缩?', default: true }).then(res=> { if (res) { compressArray(failList, filePath); } else { // 询问是否生成错误日志 } }) } else { // 压缩完成 console.log('======图片已全部压缩完成======'); } } module.exports = { compress }
libs/subCommand.js