В прошлой статье мы упоминали простой краулер с узлом, на этот раз основанный на проекте из предыдущей статьи.get_pictureПозвольте мне поделиться с вами тем, как я использовал узел для создания инструмента сжатия изображений. Оригинальная ссылкаLeeing.site/2018/10/27/…
история:«Научите вас простому безголовому инструменту Cli для краулера с узлом»
tinypng
Сначала нужно представить инструменты, на этот раз мы в основном используемtinypng
этот инструмент. tinypng — это основной инструмент сжатия изображений. Он может сжимать наши изображения с высокой точностью. Как правило, мы можем зайти на его официальный сайт.tinypng.com/Сжимайте изображения, вручную нажмите, чтобы загрузить, но за один раз можно сжать только 20 изображений, что определенно не устраивает нас, стремящихся к удобству. Нам нужно сжать все изображения сразу!
Что с этим делать? Официальный сайт tinypng очень удобен для пользователя, предоставляя различные интерфейсы, которые напрямую вызываются сервером.ДокументацияВзгляните, найдите node.js черезnpm i --save tinify
Установленный в нашем проекте, видно, что он обеспечивает множество функций, в том числе压缩图片
,resize图片
,上传cdn
Ждать. В основном мы использовали его压缩图片
,验证key
,查看已用数
.
Структура каталогов
|-- Documents
|-- .gitignore
|-- README.md
|-- package.json
|-- bin
| |-- gp
|-- output
| |-- .gitkeeper
|-- src
|-- app.js
|-- clean.js
|-- imgMin.js
|-- index.js
|-- config
| |-- default.js
|-- helper
|-- questions.js
|-- regMap.js
|-- srcToImg.js
|-- tinify.js
На основе предыдущего проекта мы добавили два новых файла
- /src/imgMin.js. то есть наш основной файл.
- /src/helper/tinify.js. В основном используется для работы связанных API-интерфейсов tinypng.
основной файл
В основном файле мы в основном используемnode
изfs模块
.
Во-первых, мы оценим, действителен ли входной ключ. Во-вторых, мы оценим, меньше ли оставшееся доступное число ключа, чем 0. Если нет проблем, мы начнем искать все файлы по пути извлечения.
путь поискаСначала мы пройдемfs.stat
Определить, является ли путь папкой, если да, передатьfs.readdir
Получите текущий список файлов, просмотрите его, а затем передайте методу получения изображения. Обратите внимание, что здесь есть яма, потому что наши операции почти все являются асинхронными операциями, поэтому я сначала использовал forEach для обхода, как само собой разумеющееся, псевдокод выглядит следующим образом
files.forEach(async (file) => {
await getImg(file);
});
Позже было обнаружено, что такой способ написания приведет к тому, что await не будет выполняться, как мы ожидали, а станет синхронным процессом (первоначально ожидалось, что одно изображение будет сжато и выведено до того, как будет выполнено второе изображение, хотя это приведет к тому, что результат очень медленный, поэтому его позже заменили синхронным сжатием), это потому, чтоforEach
Это можно понимать как передачу функции, затем внутреннее выполнение цикла, выполнение функции в цикле и возврат индекса и элемента, если входящая асинхронная функция фактически выполняется параллельно с самонастройкой нескольких анонимных асинхронных функций, поэтому ожидайте не работает, как мы ожидали. Итак, здесь мы используемfor-of
цикл, псевдокод выглядит следующим образом
for(let file of files){
await getImg(file);
}
получить фотографииПолучая картину, мы все еще проходимfs.stat
Чтобы судить, является ли текущий файл все еще папкой, мы вызываем рекурсивноfindImg
Извлеките файлы под ним.Если это изображение, сначала определите, превышает ли текущее общее количество накопленных изображений максимальное количество оставшихся изображений (если используется асинхронное сжатие, этот шаг не требуется, поскольку каждая обработка изображения ожидает предыдущее изображение для обработки.Обработка после завершения; если это синхронное сжатие, этот шаг необходим, в противном случае, если количество сжатий превышает число, это приведет к тому, что весь пакет не будет сжиматься), если не превышает, то позвонивtinify.js
серединаimgMin
Метод начинает сжиматься.
Сжать изображениеНа этом этапе мы сначала проходимfs.readFile
Прочитайте содержимое файла sourceData, а затем передайте API tinypngtinify.fromBuffer(sourceData).toBuffer((err, resultData) => {})
Метод получает сжатые данные resuleData изображения и, наконец, передаетfs.writeFile
Перезаписать исходное изображение. Следует отметить, что в async/await только при обнаружении await он будет ожидать выполнения, а за await должен следовать объект обещания, поэтому мы помещаемreadFile
,tinify.fromBuffer(sourceData).toBuffer((err, resultData) => {})
,fs.writeFile
Инкапсулируйте обещаниями.
На этом наша основная программа выполнена! Как, это все еще очень просто.
Наконец, просто добавьте нашу новую команду в команду.
Код /src/imgMin.js выглядит следующим образом:
const path = require('path');
const fs = require('fs');
const chalk = require('chalk');
const defaultConf = require('./config/default');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const stat = promisify(fs.stat);
const regMap = require('./helper/regMap');
const { validate, leftCount, imgMin } = require('./helper/tinify');
class ImgMin {
constructor(conf) {
this.conf = Object.assign({}, defaultConf, conf);
this.imgs = 0;
}
async isDir(filePath) {
try {
const stats = await stat(filePath);
if(stats.isDirectory()){
return true;
}
return false;
} catch (error) {
return false;
}
}
async findImg(filePath) {
try {
const isDirectory = await this.isDir(filePath);
if(!isDirectory){
return;
}
const files = await readdir(filePath);
for(let file of files){
// 这里不能用forEach,只能用for循环
// 加上await,则是一张张异步压缩图片,如果中间出错,则部分成功
// 不加await,则是同步发起压缩图片请求,异步写入,如果中间出错,则全部失败
// 这里为了压缩更快,采用同步写法
// await this.getImg(file);
const fullPath = path.join(filePath, file);
this.getImg(fullPath);
}
} catch (error) {
console.log(error);
}
}
async getImg(file) {
const stats = await stat(file);
// 如果是文件夹,则递归调用findImg
if(stats.isDirectory()){
this.findImg();
}else if(stats.isFile()){
if(regMap.isTinyPic.test(file)){
this.imgs ++;
const left = leftCount();
// 剩余数判断,解决同步时剩余数不足导致的全部图片压缩失败问题
if(this.imgs > left || left < 0){
console.log(chalk.red(`当前key的可用剩余数不足!${file} 压缩失败!`));
return;
}
await imgMin(file);
}else{
console.log(chalk.red(`不支持的文件格式 ${file}`));
}
}
}
async start() {
try {
const isValidated = await validate(this.conf.key);
if(!isValidated){
return;
}
const filePath = this.conf.imgMinPath;
await this.findImg(filePath);
} catch (error) {
console.log(error);
}
}
}
module.exports = ImgMin;
Код /src/helper/tinify.js выглядит следующим образом:
const fs = require('fs');
const tinify = require('tinify');
const chalk = require('chalk');
const { promisify } = require('util');
const readFile = promisify(fs.readFile);
function setKey(key) {
tinify.key = key;
}
async function validate(key) {
console.log(chalk.green('正在认证tinyPng的key...'));
setKey(key);
return new Promise(resolve => {
tinify.validate((err) => {
if(err){
console.log(err);
return resolve(false);
}
console.log(chalk.green('认证成功!'));
const left = leftCount();
if(left <= 0){
console.log(chalk.red('当前key的剩余可用数已用尽,请更换key重试!'));
return resolve(false);
}
console.log(chalk.green(`当前key剩余可用数为 ${left}`));
resolve(true);
});
});
};
function compressionCount() {
return tinify.compressionCount;
};
function leftCount() {
const total = 500;
return total - Number(compressionCount());
};
function writeFilePromise(file, content, cb) {
return new Promise((resolve, reject) => {
fs.writeFile(file, content, (err) => {
if(err){
return reject(err);
}
cb && cb();
resolve();
});
});
};
function toBufferPromise(sourceData) {
return new Promise((resolve, reject) => {
tinify.fromBuffer(sourceData).toBuffer((err, resultData) => {
if (err) {
return reject(err);
}
resolve(resultData);
})
});
};
async function imgMin(img) {
try {
console.log(chalk.blue(`开始压缩图片 ${img}`));
const sourceData = await readFile(img);
const resultData = await toBufferPromise(sourceData);
await writeFilePromise(img, resultData, () => console.log(chalk.green(`图片压缩成功 ${img}`)));
} catch (error) {
console.log(error);
}
};
module.exports = { validate, compressionCount, leftCount, imgMin };
инструмент командной строкиВ index.js добавляем следующий код
program
.command('imgMin')
.alias('p')
.option('-k, --key [key]', `Tinypng's key, Required`)
.option('-p, --path [path]', `Compress directory. By default, the /images in the current working directory are taken.
Please enter an absolute path such as /Users/admin/Documents/xx...`)
.description('Compress your images by tinypng.')
.action(options => {
let conf = {};
if(!options.key){
console.log(chalk.red(`Please enter your tinypng's key by "gp p -k [key]"`));
return;
}
options.key && (conf.key = options.key);
options.path && (conf.imgMinPath = options.path);
const imgMin = new ImgMin(conf);
imgMin.start();
});
В этой главе не будет повторяться конкретное использование слова «командующий».
Таким образом, мы выполнили наши требования, а затем обновили его до npm, мы можем передатьgp p -k [key]
для сжатия наших изображений.
Скачать проект
npm i get_picture -g
Ссылка на ссылку
- git ссылка на проектGitHub.com/1eeing/individual_…
- официальный сайт тинипнгtinypng.com/
- ссылка на команду gitGitHub.com/recommended/com мужские…