美文网首页
Squoosh-cli的代码解析

Squoosh-cli的代码解析

作者: videoisfun | 来源:发表于2023-01-01 19:28 被阅读0次

cli是libsquoosh的命令行工具,可以直接在命令行中直接运行。

Cli的核心流程分析

cli使用commander包来实现命令行界面,核心函数是processFiles,执行的关键流程是

  1. 读文件
      const buffer = await fsp.readFile(file);
  1. 解码得到image 的原始data,也就是未压缩的数据:
      const image = imagePool.ingestImage(buffer);
      await image.decoded;

ingestImage会创建一个新的Image对象,该对象的构造函数会通过WorkerPooldispatchJob post 一个decode的message到WorkerPooljobQueue中,WorkerPool则pick 一个worker,并用这个worker来做实际的解码;dispatchJob是一个异步操作, 内部会创建一个新的Promise对象,该对象会在图像解码的完成的时候,把解码得到的buffer作为返回值返回,也即该对象的实际结果。
await image.decoded就是等待解码完成。

  1. 解析preprocessors的选项:
  const preprocessOptions = {};
  for (const preprocessorName of Object.keys(preprocessors)) {
    if (!program.opts()[preprocessorName]) {
      continue;
    }
    preprocessOptions[preprocessorName] = JSON5.parse(
      program.opts()[preprocessorName],
    );
  }

这部分主要是检查是否有preprocessors的相关参数,有的话就构造一个option.

  1. 调用preprocess
  for (const image of decodedFiles) {
    image.preprocess(preprocessOptions);
  }
  await Promise.all(decodedFiles.map((image) => image.decoded));

注意image.preprocess是一个async函数,但他本身的结果或者返回值本身并不重要。它内部调用this.workerPool.dispatchJob让worker_pool来做实际的preprocess操作,并把对应的Promise赋值给image.decoded。因此在调用preprocess之后,还需要再调用await Promise.all来等待所有image.decoded的完成。

  1. 调用实际的encoder
    const job = image.encode(encodeOptions).then(async () => {
      jobsFinished++;
      const outputPath = path.join(
        program.opts().outputDir,
        path.basename(originalFile, path.extname(originalFile)) +
        program.opts().suffix,
      );
      for (const output of Object.values(image.encodedWith)) {
        const outputFile = `${outputPath}.${(await output).extension}`;
        await fsp.writeFile(outputFile, (await output).binary);
        results
          .get(image)
          .outputs.push(Object.assign(await output, { outputFile }));
      }
      progress.setProgress(jobsFinished, jobsStarted);
    });
    jobs.push(job);

注意job是一个Promise对象,它是由then()里面的异步函数创建,这个异步对象在image.encode完成异步操作后才会被触发。因此这里不需要显式等待encode对象的完成,取而代之则是等待jobs的完成。所以最后还需要等待所有job的完成。

await Promise.all(jobs);
  1. 代码清理
await imagePool.close();

用来关掉imagePool.

相关语法解析

Object

Object.keys() 返回一个对象自己的可枚举的属性名字;

// Simple array
const arr = ["a", "b", "c"];
console.log(Object.keys(arr)); // ['0', '1', '2']

// Array-like object
const obj = { 0: "a", 1: "b", 2: "c" };
console.log(Object.keys(obj)); // ['0', '1', '2']

// Array-like object with random key ordering
const anObj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.keys(anObj)); // ['2', '7', '100']

Object.values(): 返回对象可枚举的属性的值;
Object.entries():返回对象的属性的名字和值;
Object.assign(): 把源对象的属性拷贝到目标对象中,例如:

      const preprocessorOptions = Object.assign(
        {},
        preprocessors[preprocessorName].defaultOptions,
        options,
      );

相当于把defaultOptionsoptions拷贝到目标对象preprocessorOptions中;

map

map是对数组中所有元素都执行相同操作/函数,并生成一个新的数组;例如

const arr = [1, 2, 3];

const syncRes = arr.map((i) => {
    return i + 1;
});

console.log(syncRes);
// 2,3,4

map的异步版本是指操作的函数是异步的,返回的对象也是Promise,再使用Promise.all来等待所有的结果都处理完;例如

file_contents = await Promise.all(files.map(async (file) => {
  const buffer = await fsp.readFile(file);
  return buffer;
}));

这里面分成两步,file.map返回的对象是Promise对象,而Promise.all返回的则是真正的内容;

异步操作await/promise

await

await用于等待一个Promise对象实际执行完成,并返回真实操作的结果。例如
const buffer = await fsp.readFile(file)等待文件操作的实际完成,并把结果保存到buffer中。

Promise.all

await Promise.all用于同时等待多个Promise对象。

files.map(async (file) => {
      console.log("Open file " + file);
      const buffer = await fsp.readFile(file);
      const image = imagePool.ingestImage(buffer);
      await image.decoded;
      results.set(image, {
        file,
        size: (await image.decoded).size,
        outputs: [],
      });
      progress.setProgress(++decoded, files.length);
      return image;
}

这一段代码则是把file数组转换成Promise对象的数组,这个Promise对象实际返回结果是image类型。这是一种通用的模式async map:
Promise.all(arr.map(async (...) => ...))).

相关文章

网友评论

      本文标题:Squoosh-cli的代码解析

      本文链接:https://www.haomeiwen.com/subject/paxjfdtx.html