跳到主要内容

【Typecho插件】MyUpload - 图片压缩上传插件,支持TinyPNG!

· 阅读需 7 分钟

MyUpload

Typecho图片压缩上传插件,支持本地压缩和 TinyPNG 远程压缩。

插件地址:straicat/typecho-MyUpload: ?️Typecho图片压缩上传插件

使用方法

下载压缩包,将MyUpload文件夹上传到你的博客的usr/plugins/目录下,在后台启用,然后在插件设置里根据自己的需求设置插件:

如果是云主机,须安装jpegoptimpngquant

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),然后在请求头设置AuthorizationBasic 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,支持curlfsockopen

另外,还参考了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默认返回当前微秒时间。

PHP: uniqid - Manual

源码位置

其实现还是比较简单的:

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清除文件状态缓存。

PHP: clearstatcache - Manual

源码位置

因为插件原地压缩图片,也就是压缩后的图片直接覆盖原图片,所以,图片的大小发生了改变。为此,需要在压缩图片后清除文件状态缓存,这样才可以得到正确的图片大小。

这个插件涉及的技术点大概就这些,还是比较简单的,但是也挺实用。