为前端团队开发一个cli工具,实现图片压缩及阿里云oss上传

626 阅读2分钟

前端很多场景需要使用图片,图片往往都是上传到oss,每次都手动往oss去上传图片有点太过于麻烦,于是想着写一个工具可以供团队使用,做到压缩图片及上传到oss。

实现思路

我们要实现一个命令行工具首先需要解决命令行的交互,然后根据不同的参数做出不同的功能,例如-z是压缩,没有则不压缩之类的。命令行方面除了参数的功能还需要获取到图片,这个挺简单的,根据图片路径即可获取到对应图片了。

其次我们要解决图片的压缩问题,开始想使用一些node库去处理,但压缩结果感觉没有那么好,所以选择了tinypng去处理,每个月有500个免费的压缩额度,对于个人来说完全够用了。

然后就是上传到oss了,因为团队使用的是阿里云,所以这边装个阿里云的SDK,然后配置好相关密钥即可。

命令行开发

命令行的交互有几点:1、我们需要获取图片路径。2、我们需要在最开始的时候去验证是否有相关的配置,如果没有的话需要去让用户输入相关配置。3、我们需要去区分命令行参数,如-z-u之类的参数。

获取图片路径我们可以使用commander这个库,作者是koa的作者tj,区分路由参数-z-u同样也可以使用这个库来处理。

const { program } = require('commander')

const startProgram = () => {
  program
    .name('upload-cli')
    .description('welcome to use upload-cli')
    .version(`upload-cli version: ${version}`)

  // 定义两个参数-u和-z
  program
    .option('-z, --zip', 'zip your image')
    .option('-u, --upload', 'upload your image to OSS')
  program.parse(process.argv);

  // 获取到参数,参数会以对象形式返回如{zip:true}
  const options = program.opts();
  // 获取文件路径
  const filePath = program.args[0]

  return { ...options, filePath }
}

编写完上段代码我们的命令行已经可以支持-z -h -u -V四个参数了,我们先输入-h看看结果,可以看到-h就是我们常看到的命令行帮助界面,-V就是我们的版本界面,这边就不做演示了。

image.png 然后我们看看-z-u再打印一下const options = program.opts()结果看看

image.png 可以看到-z-u都换成了bool值打印出来了,由此我们可以判断用户是否添加了-z-u,然后就可以做对应处理了。然后我们可以通过 const filePath = program.args[0]这行代码去获取到图片的路径,至此我们命令行第一步的操作就结束了,实现了获取参数以及图片路径的功能。

我们的命令行还需要第二步操作,判断当前用户有没有添加配置,没有的话需要让用户输入配置。

在项目根目录我会定义一个setting.yaml文件来存储用户的配置,如TinyKey、阿里云密钥之类的。如果没有相应的配置我们需要让用户去输入。

我们先封装两个方法,分别对应读取yaml文件和写入yaml文件

const fs = require('fs')
const path = require('path')

const yaml = require('js-yaml')

const SETTING_PATH = '../conf/settings.yaml'
const PATH = path.resolve(__dirname, SETTING_PATH)

// 读取yaml文件
const readSettings = (filePath = PATH) => {
  try {
    const data = fs.readFileSync(filePath, 'utf8')
    const settingsData = yaml.load(data)
    return settingsData ? settingsData : {}
  } catch (error) {
    console.error('配置文件读取失败', error)
  }
}

// 写入yaml文件
const writeSettings = (data, filePath = PATH) => {
  try {
    const yamlStr = yaml.dump(data);
    fs.appendFileSync(filePath, yamlStr, 'utf8')
  } catch (error) {
    console.error('配置文件写入失败')
  }
}

module.exports = {
  readSettings,
  writeSettings,
}

定义好两个参数后我们可以去判断用户有没有配置了

const prompts = require('prompts')

const { readSettings, writeSettings } = require('../utils/index')

const NEED_CHECK_CONFIG = [
  { key: "TINIFY_KEY", message: "Tinify Key" },
  { key: "REGION", message: "region" },
  { key: "ACCESS_KEY_ID", message: "Access key Id" },
  { key: "ACCESS_KEY_SECRET", message: "Access key Secret" },
  { key: "BUCKET", message: "Bucket" },
  { key: "FILE_OSS_PATH", message: "文件上到OSS的目录" },
  { key: "OSS_PATH", message: "OSS的域名" }
]

const handleCheck = async (key, message = '') => {
  if (settings[key]) return
  NEED_SET_CONFIG.push({ type: "text", name: key, message: chalk.blue(`请输入你的${message}:`) })
}

const checkConfig = async () => {
  NEED_CHECK_CONFIG.forEach(item => handleCheck(item.key, item.message))
  if (NEED_SET_CONFIG.length > 0) {
    const response = await prompts([...NEED_SET_CONFIG])
    writeSettings(response)
  }
}


这边引入另外一个包prompts 这个包实现命令行对话框的形式(一问一答,用户输入)如下图

image.png 通过上面交,我们已经实现了获取图片路径、获取命令行参数、用户输入配置的功能

图片压缩

装一下tinify包然后读取相关配置看看tinify的文档即可压缩了,压缩后我们先将文件保存在本地,做上传使用

const path = require('path')
const tinify = require('tinify')
const chalk = require('chalk')

const { readSettings } = require('../utils/index.js')

const settings = readSettings()

const handleZip = async (filePath) => {
  const key = settings.TINIFY_KEY
  tinify.key = key

  const source = await tinify.fromFile(filePath);
  const basename = `[zip]${path.basename(filePath)}`;
  const downloadPath = path.resolve(filePath, '../')

  await source.toFile(`${downloadPath}/${basename}`);
  console.log(chalk.blue('图片压缩成功'))
  return downloadPath
}

module.exports = {
  handleZip
}

然后就是上传逻辑,装个包ali-oss,读取配置后,将本地文件上传即可

const path = require('path')
const oss = require('ali-oss')
const chalk = require('chalk')
const clipboardy = require('copy-paste')

const { readSettings } = require('../utils/index.js')

const settings = readSettings()

const handleUpload = (filePath) => {
  const ossConfigObj = {
    region: settings['REGION'],
    accessKeyId: settings['ACCESS_KEY_ID'],
    accessKeySecret: settings['ACCESS_KEY_SECRET'],
    bucket: settings['BUCKET']
  }

  const fileOssPath = settings['FILE_OSS_PATH']
  const ossPath = settings['OSS_PATH']

  const client = oss({ ...ossConfigObj })

  const uploadFilePath = `${new Date()
    .toISOString()
    .slice(0, 10)
    .replace(/\-/g, "")}${Math.random().toString(16).slice(2)}.${path
      .extname(filePath)
      .slice(1)}`;

  const fileName = `${fileOssPath}/${uploadFilePath}`

  client
    .put(fileName, filePath, {
      timeout: 300000,
    })
    .then(() => {
      clipboardy.copy(
        `https://${ossPath}/${fileOssPath}/${uploadFilePath}`,
        () => {
          console.log(chalk.magenta(`https://${ossPath}/${fileOssPath}/${uploadFilePath}`))
          console.log(chalk.blue('图片地址已经复制'))
        }
      );
    })
}

module.exports = {
  handleUpload
}

最后将上面代码组合起来,一个可以供团队使用的cli工具就完成了,源码地址github.com/ANNNYG/uz-i…