美文网首页
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