# 前言

真的是... 前几天我才换成 Shoka 主题,这随机的图片功能还没体验两天呢,前天突然发现这 API 就挂了... 真是有点一波三折的意思。

目前的情况是,本地调试可以,但一旦发布,那边直接就阻止访问了 —— 本地右键 -> 转到 直接取访问一样会被 403 阻止,如图所示:

不信点一点:我也忘了这是哪张图了

估计被整了域名白名单之类的东西。

Shoka 随机图片的 API 使用的是 Gimhoy 提供的图床,实际上依赖于 Sina 的服务器,而且 —— 这个功能貌似是开发者自己开发的,听说是依赖于新浪微博的相册啥功能弄的,网上搜索了一下 —— 所以其实貌似根本没有『新浪图床』这么个东西,都是玩家自己整的。

而且早在 2019 年就有文章提到这个被屏蔽了,虽然不知道为啥前几天 (大概 2022 年 12 月 28 号左右) 才又挂掉 —— 反正等了几天之后,现在还是依然是挂掉的情况,满屏 403,没啥变正常的反应。

看着没办法了,想着恐怕只能放 GitPages 一块了。

还好 Shoka 是懒加载,理论上讲就算图片慢不至于影响网页本身加载速度。

# 下载备份

因为目前本地还是可以访问『新浪图床』的,这个可能也是官方留给目前白嫖图床的各位一个备份图片的机会 —— 也会后面啥时候没准也会屏蔽这个接口。

所以趁本地还能拉图片,就想着把那批图片先拉下来。

一开始想着用 js 下载,搞了半天没整好... 后边想着不如用 C# 写个小工具,顶多可能需要在请求头模仿一下 http://localhost 的情况。

然后试了下其实没那么麻烦 —— 随便一个本地的申请接口就是可以下载的,都不需要改什么请求头,于是简单利用 HttpClient 直接在 Unity 里边写了个下载代码,通过读取 images.yml 把里面配置的所有图片都下载下来:

private static HttpClient _request;
    public static HttpClient HttpRequest
    {
        get
        {
            if (_request == null) _request = new HttpClient();
            return _request;
        }
    }
    [MenuItem("DownloadTextures/Download")]
    public async static void Download()
    {
        string path = @"D:\MyOthers\JavaScripts\WebSite1\HexoBlog\Hexo\source\_data\images.yml";
        string[] nameList = File.ReadAllLines(path);
        // 大图
        await DownLoad(nameList, "large");
        // 小图
        await DownLoad(nameList, "mw690");
    }
    private static async Task DownLoad(string[] nameList, string size)
    {
        string resPath = Path.Combine(Application.dataPath, "../Downloads");
        string localPath = Path.Combine(resPath, size);
        if (!Directory.Exists(localPath))
            Directory.CreateDirectory(localPath);
        // 下载
        foreach (var item in nameList)
        {
            string name = item.Split('-')[1].Trim();
            Debug.Log("请求下载:" + name);
            await DownLoadFile(GetUrl(name, size), localPath);
        }
    }
    private async static Task DownLoadFile(string url, string localPath)
    {
        HttpResponseMessage response = await HttpRequest.GetAsync(url);
        byte[] bytes = await response.Content.ReadAsByteArrayAsync();
        string downPicPath = Path.Combine(localPath, Path.GetFileName(url));
        File.WriteAllBytes(downPicPath, bytes);
        Debug.Log("==>OK:" + url);
    }
    private static string GetUrl(string name, string size)
    {
        return "https://tva" + randomServer + ".sinaimg.cn/" + size + '/' + name;
    }
    private static int randomServer
    {
        get
        {
            return Random.Range(1, 5);
        }
    }

这些图片前几天刚失效就下载下来了,等了两天,没看到其它谁修复的 —— 网上其它使用 Shoka 主题的基本都还是满屏 403,唯一看到一个正常的还是使用的自己的 CDN。

毕竟是微博直接屏蔽了接口,觉得这次可能是真挂了。

# 裁剪与压缩

没办法,少了这些随机的背景图,就觉得缺了不少东西,加上现在感觉 GitPages 速度也还算可以,所以决定先直接放 GitPages 算了。

不过,毕竟图片不是使用 CDN 了,能减少一点最好就减少一点,免得真卡网的时候半天都在加载,也是糟糕的体验。

于是研究了下如何把这一堆图的大小尽量减少。

# 分辨率分析

目前页面上的 cover —— 即每个文章的图片分辨率通过审查元素查出来大约是 643x224 ,实际上从新浪图床下载下来的图片,mw690 大小的至少也有 690x400

所以我们看到的图,实际能显示出来都是被『截取』过一半的,大约就只显示中间部分。

如图所示:

所以在分辨率这一块,应该是可以手动截取,减少原图大小的。

由于涉及到图片操作,我选择直接在 Unity 里面搞,毕竟这边有完整的操作接口,以前也有用过。

同时,有部分图片如果显示上半部分效果比较好,有的图片则显示中间部分比较好:虽然目前没有作处理的情况下,都是显示的中间部分,不过既然都做处理了,为何不手动调整一下?

于是简单写了两个接口:一个接取图片上半部分、一个截取图片中间部分。

当然最终实际分辨率都会是: 643x224
注:其实不一定得要这种分辨率,审查元素查出来的分辨率其实也会跟随页面大小发生变化,只要这种比例就行了,网页显示时,宽度或高度任一不足都会被放大显示

代码如下:

[MenuItem("Textures/CutTexturesTop")]
public static void CutTesturesTop()
{
    // 截取以上半部分为准
    CutTestures(false);
}
[MenuItem("Textures/CutTexturesCenter")]
public static void CutTesturesCenter()
{
    // 截取以中间部分为准
    CutTestures(true);
}
public static void CutTestures(bool isCenter)
{
    int width = 643;
    int height = 224;
    int offsetHight = isCenter ? height / 2 : height;
    string path = EditorUtility.OpenFolderPanel("选择一个待处理文件夹", "", "");
    string[] textures = Directory.GetFiles(path, "*.jpg");
    Texture2D texOrigin = new Texture2D(width, height);
    Texture2D texResult = new Texture2D(width, height);
    byte[] bytes;
    string resultPath = path + "_CutResult_CenterPixel_" + isCenter;
    if (Directory.Exists(resultPath))
        Directory.Delete(resultPath, true);
    Directory.CreateDirectory(resultPath);
    string texPath;
    int texNum = textures.Length;
    for (int i = 0; i < texNum; i++)
    {
        texPath = textures[i];
        EditorUtility.DisplayProgressBar("提示", "处理中:" + texPath, (i + 1) / (float)texNum);
        texOrigin.LoadImage(File.ReadAllBytes(texPath));
        texResult.SetPixels(texOrigin.GetPixels(0, Math.Max(0, (isCenter ? texOrigin.height / 2 : texOrigin.height) - offsetHight), width, height));
        texResult.Apply();
        bytes = texResult.EncodeToJPG(95);
        File.WriteAllBytes(Path.Combine(resultPath, Path.GetFileName(texPath)), bytes);
    }
    EditorUtility.ClearProgressBar();
}

毕竟只是临时随便用用,能用就行了,就没有花时间去处理可能的异常了。

另外,重新导出的 jpg 质量我选择了 95%,要是超过这个数,体积会增加不少,特别是 100% 质量的情况下,比不截取的图也小不了多少了。

导出然后挑挑拣拣把合适的放一块合并起来,就形成最后的结果了。

# 转化 WebP

jpg 转为 WebP 之后,大小确实还可以进一步压缩,于是找了相关工具:竟然很少有离线软件。

基本都是在线的工具。

最后在这找到一个:Webp 转换工具绿色版

# 删除与图片随机算法修改

因为没了图床加速,考虑了下还是决定把一部分删掉。

然后就有了一个新的问题:随机图片实际上也是在我们图片列表中随机,列表中图片减少,就意味着在同一个页面之中,出现同一张重复图片的概率会变得更高!

为了解决这个问题,我在主题随机代码上又进行了修改,以避免同一张图片随机在同一个页面。

具体随机方法在 engine.js 中的 _cover 里边,稍微修改了一下,做了个简单的缓存:

var cacheList=[];
hexo.extend.helper.register('_cover', function(item, num) {
  const { statics, js, image_server, image_list } = hexo.theme.config;
  if(item.cover) {
    return this._image_url(item.cover, item.path)
  } else if (item.photos && item.photos.length > 0) {
    return this._image_url(item.photos[0], item.path)
  } else {
    let bg=randomBG(num || 1, image_server, image_list);
    // 总图片数量大于 10,并且是文章图片才判断是否重复,num>1 表示页面头部图片吧,那个就不用处理了
    if(image_list.length>10&&(num==undefined||num==1))
    {
      // 图片列表走过最低页数随机后,也就是基本上全部走过一轮才允许重复随机
      let maxCheckNum=Math.floor(image_list.length/10);//Math.min(30,image_list.length);
      if(cacheList.length>=maxCheckNum){
        cacheList.length=0;
      }
      while(cacheList.indexOf(bg)!=-1){
        bg=randomBG(num || 1, image_server, image_list);
      }
      cacheList.push(bg);
    }
    return bg;
  }
})

本来只是想试一下,没想到定义在方法块外边的缓存变量真有效,那就这样了。

# 模糊

一通压缩操作下来,感觉图片质量就不大行了... 加点模糊效果吧,CSS 还挺简单的,例如:

#header {
  #imgs{
    filter: blur(1.5px);
  }
}

# 总结

压缩处理步骤:

  1. 降低分辨率,图片实际大小与实际使用分辨率保持一致,避免无谓浪费
  2. 降低部分质量,例如截取后存储质量我选择 95%
  3. WebP 化
  4. 利用 TinyPng 再压一遍 (注:最后实测发现转 WebP 时压缩率高了实际上 TinyPng 也压不动了)
  5. 质量不行就用模糊什么的挡一下

总之,就是在尽量『能看』的情况下,减少传输数据量,毕竟 GitPages 的速度... 谁知道什么时候快慢呢...

这么就几个步骤下来,基本上可以减少到极致了 (也许还有其它再减少大小的处理方式?如果有知道的务必留言提醒!)

今天是 2023 年 1 月 1 日 2023 年 1 月 2 日,我竟然在研究如何优化博客... 图片怎么处理...😥