【Typecho插件】MyUpload - 图片压缩上传插件,支持TinyPNG!
MyUpload
Typecho
图片压缩上传插件,支持本地压缩和 TinyPNG 远程压缩。
插件地址:straicat/typecho-MyUpload: ?️Typecho图片压缩上传插件
使用方法
下载压缩包,将MyUpload文件夹上传到你的博客的usr/plugins/目录下,在后台启用,然后在插件设置里根据自己的需求设置插件:
如果是云主机,须安装jpegoptim
和pngquant
。
Ubuntu系统下安装方法:
$ sudo apt install jpegoptim pngquant
如果是虚拟主机,可以采用远程压缩,须先到https://tinypng.com/developers注册一个 API Key:
图片远程压缩速度很慢,请耐心等待(有可能超时导致压缩失败)。
说明
写博客时,如果不压缩图片,既比较费主机存储空间,还会非常拖慢页面加载速度,特别是对于带宽小的主机。可是,如果要压缩好图片后再上传又比较麻烦,放到对象存储上还另外要钱。于是乎,就撸了这个插件,在上传时自动压缩图片。压缩图片采用的方法是调用jpegoptim
压缩 jpg 图片,调用pngquant
压缩 png 图片,我觉得压缩效果挺好的,可以基本不降低清晰度且大幅降低图片占用空间。
UPDATE1:由于部分人反映希望能支持虚拟主机,那就加上远程压缩的功能吧,使用 TinyPNG 远程图片压缩服务,一个月免费压缩500张,应该是够用的。
除此之外,由于Typecho
默认的图片重命名方式理论上可能出现重名覆盖,而且个人觉得把图片按月分文件夹有点麻烦,喜欢按年分文件夹。因此,对图片的重命名方式做了修改,不会出现重名覆盖,并且图片文件名也还是很短的,图片可以按年分文件夹,也按月分文件夹(和Typecho
默认一样)。当然,这个需求比较小众,默认是保持Typecho
默认的方式。
v1.5 开发笔记
本次增加了 TinyPNG 远程压缩服务,官网:TinyPNG – Compress PNG images while preserving transparency
API 文档:TinyPNG – API Reference
TinyPNG 采用 HTTP Basic Auth 做授权认证,还是非常简单的。只要把api:YOUR_API_KEY
做一个Base64
转换(假设为BASE64_KEY
),然后在请求头设置Authorization
为Basic BASE64_KEY
就可以了。
例如,假设YOUR_API_KEY是TuqxTXbr6NH3k4NptoqRpqcVXaJRbWSf
(我随机生成的),进行Base64
转换时注意别带上结尾的换行了,用 Shell 可以这么转换:
$ echo -n api:TuqxTXbr6NH3k4NptoqRpqcVXaJRbWSf | base64
-n
参数就是不带结尾的换行。在 PHP 里的转换方法:
$apiKey = 'TuqxTXbr6NH3k4NptoqRpqcVXaJRbWSf';
$token = base64_encode('api:' . $apiKey);
另外,Typecho
封装了一个 HTTP 客户端,也就是Typecho_Http_Client
,支持curl
和fsockopen
。
另外,还参考了PHP: POST 方法上传 - Manual,PHP 毕竟是最好的语言,对于文件上传还专门有个$_FILES
全局变量。
v1.2 开发笔记
几个月前从Hexo
迁移到Typecho
时就写了这个插件,由于懒癌晚期现在才写文。
插件压缩图片的方式非常简单粗暴,直接调用shell命令。png 压缩的命令是:
$ pngquant --skip-if-larger --ext .png --force --quality=60-90 xxx.png
jpg 压缩的命令是:
$ jpegoptim --max=90 --preserve --all-progressive xxx.jpg
虽然解决方案不够优雅,但不管黑猫白猫,抓到老鼠的就是好猫。这插件我自己使用了一段时间,感觉还是比较实用的,毕竟这主机小水管。
Typecho
会对图片重命名,重命名的方式如下:
$fileName = sprintf('%u', crc32(uniqid())) . '.' . $ext;
crc32
的碰撞概率还是挺高的,不过,Typecho
将图片按月归档,所以还好。不过,我还是有强迫症,为了杜绝这种概率,我使用了另外的重命名方式:
sprintf('%s', base_convert(uniqid(), 16, 36)) . '.' . $ext;
利用36进制缩短地址长度,图片名依然不长却避免了碰撞(尽管概率基本可视为0)。
uniqid
uniqid
默认返回当前微秒时间。
其实现还是比较简单的:
do {
(void)gettimeofday((struct timeval *) &tv, (struct timezone *) NULL);
} while (tv.tv_sec == prev_tv.tv_sec && tv.tv_usec == prev_tv.tv_usec);
prev_tv.tv_sec = tv.tv_sec;
prev_tv.tv_usec = tv.tv_usec;
sec = (int) tv.tv_sec;
usec = (int) (tv.tv_usec % 0x100000);
if (more_entropy) {
uniqid = strpprintf(0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg() * 10);
} else {
uniqid = strpprintf(0, "%s%08x%05x", prefix, sec, usec);
}
首先是获得当前微秒级时间,这里有点像 CAS 操作,不断循环直至时间变化,这样可以确保单线程下的唯一性。微秒时间对 1048576 取余(限制一下数字范围以避免转为字符串时超出字符?),最后把前缀、秒、微秒,还有可能的额外的熵拼接起来。额外熵是使用线性同余算法生成的随机数。
clearstatcache
clearstatcache
清除文件状态缓存。
因为插件原地压缩图片,也就是压缩后的图片直接覆盖原图片,所以,图片的大小发生了改变。为此,需要在压缩图片后清除文件状态缓存,这样才可以得到正确的图片大小。
这个插件涉及的技术点大概就这些,还是比较简单的,但是也挺实用。