TingPng interface compression, support compression failure to compress, support command line call, support NPM installation call. Don’t say much, do something.
Compression show
Tools you need to use
Listr provides a list of terminally executable tasks cli-table’ allows unicode auxiliary tables (i.e. table rendering results) to be rendered on the command line from the Node.js script Generate command line commands.
The directory structure
Project │ README. Md │ package. Json. │ gitignore └ ─ ─ ─ bin │ │ esigntiny └ ─ ─ ─ the demo | | │ images │ index. The js └ ─ ─ ─ the lib | │ Index. js | │ inittiny.js | │ esigntiny.js | │ compresstiny.js | │ compressfailedimg.js | │ resultToChalk. Js | │ utilsCopy the code
Bin is used to store the executable Node file, which is used to invoke mode on the command line. Demo provides scripts that can be run directly and images that need to be compressed. Lib is the code source file.
Commander Generates a command line command
#! Run the /usr/bin/env node command in //node
const program = require('commander')
const esigntiny = require('.. /lib')
const path = require('path')
const argv = require('minimist')(process.argv.slice(2))
let input = argv.i || argv.input
let output = argv.o || argv.output
const { getCwd } = require('.. /lib/utils.js')
// Command line prompt
program
.version(require('.. /package.json').version, '-v --version')
.usage('<command> [options]')
.option('-i, --input'.'input directory [require]')
.option('-o, --output'.'output directory')
// Register the start command
program.command('start').description('start compress images').action(start)
// Parse the command line
program.parse(process.argv)
Enter esigntiny start on the command line to execute this function
function start() {
if(! input) {console.log('require input directory')
return
}
input = path.resolve(getCwd(), input)
if(!!!!! output) output = path.resolve(getCwd(), output) esigntiny({input: input,
output: output,
})
}
if(! program.args.length) { program.outputHelp() }Copy the code
Lib code analysis
The implementation logic is very simple. First, obtain the image address of the input recursively, then call tinyPng’s interface to compress and get the compressed image address, and then request the image address to be written into the target file.
lib/index.js
const { normalizeOptions } = require('./utils.js')
const initTiny = require('./initTiny.js')
const compressTiny = require('./compressTiny.js')
const esigntiny = async function (options) {
// Initializes user parameter transmission
options = normalizeOptions(options)
// 1. Initialize the program recursively to get the image address
// 2. Compress images
const taskExample = compressTiny(initTiny(options))
taskExample.run().catch((err) = > console.log(err))
}
module.exports = esigntiny
Copy the code
Index.js is used to normalize parameters, create listr instances, and create tasks that fetch and compress images.
initTiny
const Listr = require('listr')
// Get the image address recursively
const { getImsges } = require('./utils.js')
module.exports = function (options) {
const taskExample = new Listr()
taskExample.add(getFiles(options))
return taskExample
}
function getFiles(options) {
return {
title: 'Get all images'.task: (ctx, task) = > {
ctx.options = options
ctx.images = getImsges(options.input)
task.title = ` were found${ctx.images.length}Picture `
if (ctx.images.length === 0) {
Promise.reject('Picture not found')}},}}Copy the code
compressTiny
const resultToChalk = require('./resultToChalk.js')
const compressFailedImg = require('./compressFailedImg.js')
const { tinyFun } = require('./utils.js')
module.exports = function (taskExample) {
taskExample.add({
title: 'Compress image'.task: async (ctx, task) => {
// Get compression results for all images
const imagesRsult = await Promise.all(tinyFun(ctx))
// Failed to compress the image at the same time
const failedList = await resultToChalk(imagesRsult)
await compressFailedImg(failedList, ctx.options)
},
})
return taskExample
}
Copy the code
Get the compression result of all images, then draw the result table, and then enable the image compression again.
TinyFun image compression
/** * Compress each file returns a promise * compressFile wraps each request link */
const tinyFun = (ctx) = > {
const { images, options } = ctx
return images.map((item) = > {
return compressFile(item, options)
})
}
/** * compressed file */
const compressFile = (filePath, options) = > {
return new Promise((resolve, reject) = > {
createReadStream(filePath).pipe(
request(parms, (res) = > {
res.on('data'.async (info) => {
try {
info = JSON.parse(info.toString())
// console.log('[[[[[]]]]]', info)
if (/^\s*</g.test(info) || info.error) {
resolve(
getMessage({
info,
filePath,
msg: 'Compression failed'.code: 500,}))return
}
resolve(await getImageData(info, options, filePath))
} catch (e) {
// console.log(e, '))0')
resolve(
getMessage({
info,
filePath,
msg: 'Interface request rejected'.code: 500,}))}})}))})})}/** * read the image, write the file */
const getImageData = (imageInfo, options, filePath) = > {
let output = options.output
const input = options.input
const imageUrl = imageInfo.output.url
const oldSize = (imageInfo.input.size / 1024).toFixed(2)
const newSize = (imageInfo.output.size / 1024).toFixed(2)
return new Promise((resolve, reject) = > {
get(imageUrl, (res) = > {
const outDir = path.dirname(output)
output = filePath.replace(input, output)
if(! existsSync(outDir)) { mkdirSync(outDir) } res.pipe(createWriteStream(output)) res.on('end'.function () {
resolve(
getMessage({
code: 200,
filePath,
msg: 'Compression succeeded'.info: {
oldSize,
newSize,
imageUrl,
},
})
)
})
})
})
}
/** * interface text prompt */
const getMessage = ({ msg, code, info, filePath }) = > {
return {
code: code || 400.msg: msg || 'success'.data: {
filePath,
info,
},
}
}
Copy the code
CompressFile wraps each request link and creates a readable stream request tinyPng compressed image. Resolve all results and hand error handling to resultToChalk. Compression success returns the image address after compression success, create request to write the specified file address.
ResultToChalk Indicates the result
const Table = require('cli-table')
const chalk = require('chalk')
const path = require('path')
const { sleep } = require('./utils.js')
const headArr = {
head: ['name'.'status'.'old-size(kb)'.'new-size(kb)'.'compress ratio(%)'],}// const table = new Table({
// head: ,
// })
// Get the data to print
const getSuccessInfo = (result) = > {
const data = result.data
const info = data.info
const fileName = path.basename(data.filePath)
const compressRatio = parseFloat(
((info.oldSize - info.newSize) / info.oldSize) * 100
).toFixed(2)
return [fileName, 'success', info.oldSize, info.newSize, compressRatio]
}
module.exports = async function (imagesRsult) {
let totalNewSize = 0,
totalOldSize = 0,
successNum = 0,
failedNum = 0,
failedList = [],
table = new Table(headArr)
if (imagesRsult && imagesRsult.length) {
imagesRsult.forEach((result) = > {
const filePath = result.data.filePath
if (result.code === 200) {
totalNewSize += +result.data.info.newSize
totalOldSize += +result.data.info.oldSize
successNum += 1
table.push(getSuccessInfo(result))
} else {
const fileName = path.basename(filePath)
failedNum += 1
failedList.push(filePath)
table.push([fileName, 'failed'])}})}await sleep(1000)
console.log(table.toString())
const resStr = 'Total number of pictures:${ imagesRsult.length }, compression success:${successNum}, compression failed:${failedNum}, compression ratio:${
((totalOldSize - totalNewSize) / totalOldSize).toFixed(2) * 100
}` (%)
console.log(chalk.red(resStr))
// Enable failed compression after 2 seconds
await sleep(2000)
return failedList
}
Copy the code
CompressFailedImg fails and compresses again
const { tinyFun } = require('./utils.js')
const Listr = require('listr')
const resultToChalk = require('./resultToChalk.js')
let compressTimes = 1
// Start a new Listr and create an image compression task
module.exports = async function compressFailedImg(failedList, options) {
const taskExample = new Listr()
if (compressTimes-- > 0) {
taskExample.add({
title: 'Failed to compress image again'.task: async (ctx, task) => {
const imagesRsult = await Promise.all(
tinyFun({
images: failedList,
options,
})
)
// Failed to compress the image at the same time
await resultToChalk(imagesRsult)
},
})
taskExample.run().catch((err) = > {
console.log(err)
})
}
}
Copy the code
CompressFailedImg starts the new compression Listr, also calls tinyFun to get the compressed result, which is then handed to resultToChalk for processing.
Ok, basically complete the compression tool, gitHub address I hope you can enjoy.