Published on

สร้าง Command-Line เปลี่ยน Type ของ Image ด้วย Sharp

สร้าง Command-Line เปลี่ยน Type ของ Image ด้วย Sharp
สร้าง Command-Line เปลี่ยน Type ของ Image ด้วย Sharp

อันยองงงง บทความวันนี้เกิดจาก ปกติบทความใน https://kajame.xyz รูปภาพจะเป็น .webp แต่รูปที่ capture จากจอปกติแล้วได้เป็น .png มา ก็เลยต้องไปแปลงก่อน แล้วบทความที่เขียนไปก่อนหน้า รูปเยอะซะด้วย เลยแปลงได้ไม่หมด เลยลองไปดูว่ามี lib อะไรแปลงไฟล์ได้ เลยเป็นที่มาของบทความนี้ครับ

ขอเกริ่นอีกนิด ฮ่าาาา! ปกติแล้วถ้าใช้คนเดียวเจมส์จะเขียนเป็นแบบเรียกง่าย ๆ แต่พอจะเขียนบทความนี้เลยเอาให้มันดีขึ้นมาอีกนิดนึงดีกว่า เพื่อไม่ให้เป็นการเสียเวลามาเริ่มกันเลยดีกว่าครับ

มาเริ่มกันเลย

ก่อนจะเริ่มต้น เรามาดูผลลัพธ์ที่อยากได้ก่อนดีกว่าครับ ภาพที่เสร็จแล้วเจมส์อยากให้เวลาเรียกใช้งานเรียกใช้แบบนี้ได้เลย

[ชื่อ module ที่เราสร้าง] [โฟลเดอร์ที่มีรูปที่ต้องการแปลง] [โฟลเดอร์ที่ต้องการให้รูปที่แปลงมาใส่] [type ที่เราจะแปลง]

เช่น

picTransform ./my-picture ./convert webp

ขั้นแรกเราจะสร้าง Project ขึ้นมาก่อนเน้อครับ ในที่นี้เจมส์จะสร้างชื่อว่า picTransform เลย และเข้าไปใน directory

mkdir picTransform && cd picTransform

จากนั้นให้เรา init package.json ขึ้นมาโดยใช้คำสั่ง

npm init -y

จากนั้นสร้าง directory ชื่อ bin ขึ้นมาครับ

mkdir ./bin

จากนั้นเข้าไปใน directory ชื่อ bin

cd ./bin

แล้วสร้างไฟล์ชื่อ index.js ขึ้นมาครับ

จากนั้นลองใส่ code ใน index.js ดังนี้ครับ

./picTransform/bin/index.js
#! /usr/bin/env node
console.log("Pic transform");

จากนั้นให้แก้ไฟล์ package.json โดยในส่วน คีย์ที่ชื่อ mainให้ใส่ค่าเป็น ./bin/index.js

และให้เพิ่มคีย์ชื่อ bin โดยใส่ค่าเป็น { "picTransform": "./bin/index.js" } ค่าจะเป็นประมาณนี้ครับ

./picTransform/package.json
{
  "name": "pictransform",
  "version": "1.0.0",
  "description": "",
  "main": "./bin/index.js",
  "bin": {
    "picTransform": "./bin/index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

จากนั้นให้เราอยูที่ root project จากนั้นใช้คำสั่ง

npm install -g .

จากนั้นลองใช้คำสั่ง

picTransform

จะพบว่ามันจะแสดงข้อความว่า Pic transform ตามที่เราใส่ไว้ใน index.js

ใช้ Yarg ช่วยสร้าง Cli

ขั้นแรกเดี๋ยวเราจะ install yarg โดยใช้คำสั่งดังนี้ ที่ root project

npm install --save yargs

จากนั้นให้แก้ไขไฟล์ index.js ใน bin โดยปรับเป็นประมาณนี้ครับ

./picTransform/bin/index.js
#! /usr/bin/env node
const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers");
const argv = yargs(hideBin(process.argv)).argv;
const [inputDir, outDir, imageType] = argv._;

console.log(`inputDir: ${inputDir}`);
console.log(`outDir: ${outDir}`);
console.log(`imageType: ${imageType}`);

จากนั้นเราจะลองเรียกตามรูปแบบที่เราเกริ่นไว้ตอนแรกที่อยากจะทำคือ

picTransform ./my-picture ./convert webp

จะพบว่า output ที่แสดงออกมาจะเป็นดังนี้

inputDir: ./my-picture
outDir: ./convert
imageType: webp

โอเคตอนนี้เรารับ parameters ที่ได้จาก arguments ที่ส่งมาตอนเรียกจาก command ได้เรียบร้อยแล้ว

ใช้ Sharp แปลงไฟล์เป็น Type ที่กำหนด

ในบทความนี้เราจะเขียนให้แปลงได้แค่เป็น jpg กับ webp เท่านั้นเน้อครับ คุณผู้อ่านสามารถไปดู doc ของ sharp แล้วทำเพิ่มเติมต่อยอดได้เลย ^^

ขั้นแรกให้เรา install sharp มาใส่ใน project ของเราก่อนเน้อครับ โดยไปอยู่ที่ root project แล้วใช้คำสั่ง

npm install --save sharp

จากนั้นเราจะแก้ไขโค้ดของ index.js ให้เป็นแบบนี้ครับ

./picTransform/bin/index.js
#! /usr/bin/env node
const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers");
const { readFileFromDirectory, changeImageType } = require("./helper");
const argv = yargs(hideBin(process.argv)).argv;
const [inputDir, outDir, imageType] = argv._;

async function main() {
  try {
    const imagesName = await readFileFromDirectory({ inputDir });
    changeImageType({ inputDir, outDir, imagesName, imageType });
  } catch (error) {
    console.log(error.message);
  }
}

main();

จากนั้นให้เราเพิ่มไฟล์ชื่อ helper.js ขึ้นมาครับ และใส่โค้ดลงไปดังนี้ครับ

./picTransform/bin/helper.js
const fs = require("fs");
const sharp = require("sharp");

function readFileFromDirectory({ inputDir }) {
  return new Promise((resolve, reject) => {
    fs.readdir(inputDir, async (err, imagesName) => {
      if (err) {
        return reject(err);
      }

      return resolve(imagesName);
    });
  });
}

function changeImageType({ inputDir, outDir, imagesName, imageType }) {
  let fileType;
  let sharpChangeType;

  if (imageType === "jpg" || imageType === "jpeg") {
    fileType = ".jpg";
    sharpChangeType = (input) => {
      return sharp(input).jpeg({
        quality: 100,
        chromaSubsampling: "4:4:4",
      });
    };
  } else if (imageType === "webp") {
    fileType = ".webp";
    sharpChangeType = (input) => {
      return sharp(input).webp({ lossless: true });
    };
  }

  return imagesName.map((imageName) => {
    fs.readFile(`${inputDir}/${imageName}`, async (err, input) => {
      if (err) {
        console.log(`changeImageType: ${err.message}`);
        return;
      }

      let imgName = imageName.split(".");
      imgName.pop();
      imgName = `${imgName.join(".")}${fileType}`;

      try {
        await sharpChangeType(input).toFile(`${outDir}/${imgName}`);
        console.log(`filename: ${imageName}, status: Done`);
      } catch (error) {
        console.log(`filename: ${imageName}, status: Error, ${error.message}`);
      }
    });
  });
}

module.exports = {
  readFileFromDirectory,
  changeImageType,
};

มาทำความเข้าใจโค้ดกันหน่อย

โค้ดใน index.js สิ่งที่ทำคือรับ params ที่เราลองรับมาก่อนหน้านี้ คือ directory ของรูปภาพที่ต้องการแปลง, directory ที่ต้องการเอารูปที่แปลงแล้วไปใส่, ชนิดของไฟล์ที่ต้องการแปลง

จากนั้นจะอ่านข้อมูลใน directory ของรูปภาพที่ต้องการแปลง ซึ่งจะได้ข้อมูลคือชื่อรูปภาพที่ต้องการแปลงที่อยู่ใน directory

จากนั้นเราจะส่งก้อนข้อมูลนี้คือ directory ของรูปภาพที่ต้องการแปลง, directory ที่ต้องการเอารูปที่แปลงแล้วไปใส่, ชนิดของไฟล์ที่ต้องการแปลง, ข้อมูลรูปภาพที่ดึงได้จาก directory ของรูปภาพที่ต้องการแปลง โยนไปที่ฟังก์ชัน changeImageType

ในฟังก์ชันนั้นจะเช็คว่า imageType ที่ต้องการแปลงคือ type อะไร และสร้างฟังก์ชันสำหรับรับ input ที่เป็น buffer เข้ามา และแปลงเป็น type นั้นๆตามที่ได้กำหนดใน imageTye

จากนั้นเราจะเอา array ของ รูปภาพที่ส่งเข้ามามาดึงข้อมูลเป็น buffer และส่งไปแปลงใน ฟังก์ชันที่เราสร้างไว้ก่อนหน้า (sharpChangeType) แล้วก็ print แสดงข้อมูลว่ารูปไหนแปลงได้ หรือแปลงไม่ได้

การเรียกใช้งาน

picTransform ./my-picture ./convert webp

จากตัวอย่างเจมส์ คือต้องสร้าง directory ชื่อ convert ไว้ด้วยเน้อครับ เนื่องจากในโค้ดเจมส์ไม่ได้เขียนเช็คไว้ให้ว่า ถ้าไม่มี directory ของ outDir ให้สร้างให้

ไฟล์ที่อยู่ใน ./my-picture จะถูกแปลงและใส่ใน directory ชื่อ convert โดยจะแปลงไฟล์เป็น .webp ซึ่งชื่อที่แปลงมาจะใช้ชื่อเดิมจากก่อนแปลง

หวังว่าบทความนี้จะมีประโยชน์กับคุณผู้อ่านทุกท่านเน้อครับ หากมีส่วนไหนผิดพลาดประการใดก็ขออภัยมา ณ ที่นี้ด้วยเน้อครับ

Github: https://github.com/jame3032002/picTransform

npm: https://github.com/jame3032002/picTransform