前端很多场景需要使用图片,图片往往都是上传到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就是我们的版本界面,这边就不做演示了。
然后我们看看
-z和-u再打印一下const options = program.opts()结果看看
可以看到
-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 这个包实现命令行对话框的形式(一问一答,用户输入)如下图
通过上面交,我们已经实现了获取图片路径、获取命令行参数、用户输入配置的功能
图片压缩
装一下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…