开源地址
github:https://github.com/liujinpen/picgo-plugin-compress-tinypng.git
npm:https://www.npmjs.com/package/picgo-plugin-tinypng
前言
在写个人博客时,需要上传图片到图床,PicGo是相当不错的工具,但使用过程中发现图片体积较大时,会浪费云存储的空间,因为不需要如此高的清晰度。在之前,较大的图片文件我都选择手动压缩,非常麻烦。
好在PicGo本身支持插件,在搜索之后发现以后已经有支持压缩的插件了:picgo-plugin-compress,但是PicGo-Core从v1.5.0
开始,request包底层从Request-Promise-Native 转移到了axios,因此该插件在PicGo客户端2.3.0版本之后使用会报错,且作者长时间并未更新。
在了解了tinify后,决定开发一个适配PicGo的图片压缩插件。
压缩效果
tinify官方:压缩后的图片对视觉的影响几乎不可见,但是在文件大小上有非常大的差别。
这里测试样本较少,但已经可以感受到效果了:
-
原始图片:1.78MB -> 压缩后:511.9KB
-
原始图片:27.4KB -> 压缩后:11.5KB
tinify-API使用
tinify为开发者提供了多种api的使用方法,可以注册邮箱获取API key,一个API key每个月有500张免费次数可用,具体API的使用可以参考https://tinify.cn/developers。
PicGo插件开发
官方插件开发文档中较为详细的说明了开发流程,需要有一定的js基础。
1.使用插件模板
使用插件模板首先需要全局安装picgo
npm install picgo -g
初始化项目
picgo init plugin tinypng
注:tinypng为项目名称
创建过程可以根据需要自定义选项,从上到下依次是:
- 插件名称
- 插件描述
- 作者
- 所开发插件对应的部件(详见文档),压缩图片需要做的是将图片转化为buffer提供给picgo,因此这里选择transformer部件
- 是否CLI插件(如果需要在图形客户端中使用,选否)
- 使用TypeScript还是JavaScript(官方推荐TS可以有更好的语法提示,实际上在WebStorm中开发的话,使用JS的体验也非常好)
- 插件是否要配置快捷键
项目创建完毕后可以使用IDE进行开发。
2.插件配置项
使用tinify的API需要key,因此在插件中配置入口:
const config = ctx => {
let config = ctx.getConfig('tinypng') || ctx.getConfig('picgo-plugin-tinypng')
if (!config) {
config = {}
}
ctx.log.info('读取config:', config)
return [{
name: 'key', // 配置名
type: 'input', // 配置类型,input 展示为一个输入框
default: config.key || null, // 默认值
required: true, // 是否必填
message: '填写tinypng的API Key' // 占位符
}]
}
3.压缩文件
我们在PicGo拖入一张本地图片时,获取到的是本地文件的路径。
压缩的逻辑:
- 使用fs-extra库的
readFile
方法将图片转换为二进制流; - 构造tinify需要的请求(格式化后的API key,文件buffer,其他参数等)
- 利用PicGo的request库发送请求
- 获取tinify返回的压缩后的图片地址
- 利用PicGo的request库下载该图片(获取的是文件buffer)
- 构造transformer部件需要的输出格式
至此,插件的工作已经完成了大部分,将我们构造的结果返回,PicGo就可以将压缩后的文件上传至图床。
/**
* 读取本地图片,上传至tinypng压缩,获取压缩后url,下载图片buffer
*
* @param ctx PicGo
* @param imageUrl 本地图片路径
* @param key 配置中的api key
* @returns {Promise<Buffer>}
*/
async function tinypngKeyCompress(ctx, imageUrl, key) {
return await fs.readFile(imageUrl).then(fileData => {
// 1.构造请求参数(向tinypng请求)格式参考https://tinify.cn/developers/reference
const bearer = Base64.stringify(Utf8.parse(`api:${key}`))
const fetchOptions = {
method: 'POST',
url: 'https://api.tinify.com/shrink',
json: true,
resolveWithFullResponse: true,
headers: {
Host: 'api.tinify.com',
Authorization: `Basic ${bearer}`
},
data: fileData
}
// 2.发送压缩请求
return ctx.request(fetchOptions).then(response => {
// 3.获取压缩后图片的url
if (response.status && response.status >= 200 && response.status <= 299) {
const location = response.headers.location
ctx.log.info('压缩后的地址:', location)
// 4.从url中获取网络图片
return utils.fetchImage(ctx, location).then(res => {
// 5.获取压缩后的图片buffer,构造picGO需要的格式
return utils.getImageInfo(imageUrl, res.data)
})
}
throw new Error('压缩出错')
})
})
}
相关工具类
/**
* 获取图片信息
* @param imageUrl
* @param buffer
* @returns {{fileName: string, width: number, buffer, extname: string, height: number}}
*/
function getImageInfo(imageUrl, buffer) {
const {width, height} = imageSize.imageSize(buffer)
return {
buffer,
width: width, //宽度
height: height, //高度
fileName: path.basename(imageUrl), // 文件名
extname: path.extname(imageUrl), //扩展名
}
}
/**
* 根据url获取文件信息
* @param imageUrl
* @returns {{fileName: string, extname: string}}
*/
function getUrlInfo(imageUrl) {
return {
fileName: path.basename(imageUrl),
extname: path.extname(imageUrl)
}
}
/**
* 下载网络图片
* @param ctx
* @param url
* @returns {*}
*/
function fetchImage(ctx, url) {
return ctx.request({
method: 'GET',
url,
encoding: null,
resolveWithFullResponse: true,
responseType: 'arraybuffer'
})
}
4.完成插件流程
这部分的代码需要按照PicGo插件开发要求进行组织
module.exports = (ctx) => {
const handle = ctx => {
// 获取配置的key
const config = ctx.getConfig('tinypng') || ctx.getConfig('picgo-plugin-tinypng')
const key = config['key']
// input是一个路径列表,对每个路径的图片都先进行压缩
const tasks = ctx.input.map(imageUrl => {
ctx.log.info('图片地址:' + imageUrl)
// 调用压缩方法
return tinypng.tinypngKeyCompress(ctx, imageUrl, key).then(res => {
return res
})
})
return Promise.all(tasks).then((output) => {
ctx.output = output
return ctx
})
}
const config = ctx => {
let config = ctx.getConfig('tinypng') || ctx.getConfig('picgo-plugin-tinypng')
if (!config) {
config = {}
}
ctx.log.info('读取config:', config)
return [{
name: 'key', // 配置名
type: 'input', // 配置类型,input 展示为一个输入框
default: config.key || null, // 默认值
required: true, // 是否必填
message: '填写tinypng的API Key' // 占位符
}]
}
const register = () => {
ctx.helper.transformer.register('tinypng', {handle})
}
return {
transformer: 'tinypng',
register,
config
}
}
本地测试
如果开发模板使用的是JS,在开发完后可以直接在PicGo主界面->插件设置->右上角导入图标->选择源码目录。
是的,选择源码目录,PicGo便能成功加载插件,加载完成后需要重启生效。
导入插件后,需要启用transformer-tinypng选项
(显示禁用时代表启用成功):
配置plugin -picgo-plugin-tinypng
选项中可以填入申请的API key以及图床配置名(自定义):
NPM发布
本文的插件已经发布,在PicGo的插件设置搜索tinypng即可。
npm发布可以参考如何发布自己的npm包。
这样别人就可以在PicGo的插件设置中搜索到了。
参考
PicGo配置图片压缩(已不可用)
picgo-plugin-compress(已停更插件)