2023.09.17 更新:大更 API,现在支持多个图片集。

2923.09.24更新: 玩转云服务(9):使用 Cloudflare Workers + D1 + R2 搭建免费的随机图片 API


我 WordPress 博客站使用的主题 Sakurairo 最大特点就是花里胡哨,比如首页的随机全屏封面图。不过我之前的全屏封面图使用的是本地图,也就是直接把一大坨二次元图片上传到 WordPress 所在的服务器上,Sakurairo 自己随机抽取一张做首页封面或文章头图。但是这样有以下不足:

  • 不知为什么只支持 jpg,上传 webp 直接不显示,而 webp 的文件体积可是比同质量的 jpg 小上一半的;
  • 不能按照客户端类型随机,比如桌面端返回横图,手机返回竖图。

如果白嫖其他人的随机图 API,别人的图也不一定是我想要的,质量也参差不齐。为了用上我花费几年时间人工筛选、精心收藏的千张美图,那就自己实现一个随机图片 API 吧!

注意

虽然我通过精心设计,尽力消除可能的漏洞,但是由于个人知识限制,仍不能保证百分之百的安全性。特别是高级版 API 允许通过查询字符串传递 SQL WHERE 条件,可能会有我未预想到的风险。

安装 Tailscale

教程:大内网战略(1):使用 Tailscale 搭建 VNet

在服务器和你的个人电脑上都安装 Tailscale,只通过内网来刷新数据库信息(否则别人公网访问随意蹂躏你的数据库那也太恐怖了)。

域名解析

我们需要三个域名:

  • 图片存储域名:所有的图片属于这里,下文用 pictures.mycdn.com 指代,解析到服务器公网 IP;
  • 图片扫描域名:只能内网访问,扫描图片并且将信息写入到数据库,下文用 pictures-admin.mycdn.com 指代,解析到服务器 Tailscale 内网 IP
  • API 域名:下文用 pictures-api.mycdn.com 指代,解析到服务器公网 IP。

这三个域名可以都是同一个二级域名衍生的三级域名。

下面是我在 Cloudflare 的域名解析。

强烈建议开启小黄云 CDN 代理

建立图片网站

下面使用宝塔。

图片存储网站

首先建立网站 pictures.mycdn.com ,然后在网站配置下添加以下内容并保存:

    #禁止访问目录
    location ^~ /admin/ {
        return 403;
    }
    #禁止访问目录下文件
    location ^~ /admin {
        return 403;
    }
这段配置是防止有人顺藤摸瓜,从公网访问扫描代码和数据库凭据

然后在网站根目录下建立目录:admin 和图片目录。可以建立多个图片目录,来划分为多个图片集,比如我的:

重要

如果上传的照片中有不适宜的图片,需要区分使用,可以如下图示例往文件名中添加 bjn ,后续使用 API 时可以指定查询关键字 nobjn 或者 bjn = 0 过滤。

提示

出于流量与带宽考虑,强烈不建议上传过大的图片(特别是如果打算分享 API 给多人使用 )。可以使用 ImageBatchResizer 压缩图片。

至于 admin 文件夹,你需要下载 pictures-scan-admin 里的文件丢进去。其中,scan_config.php 文件我们待会要修改,暂且按下不表。

建议给这个域名上 SSL 证书,比如我用的是 CF 15年超长有效期证书。

图片扫描网站

再新建一个网站 pictures-admin.mycdn.com,目录和上面的 pictures.mycdn.com同一个目录

添加并保存以下配置:

    listen <服务器 Tailscale IP>:80;

    location = / {
        rewrite ^ /admin/scan.php last;
    }
这段配置是让这个管理网站只监听内网 IP,彻底断绝公网 IP 访问入口
这段配置是映射,只需要域名就能访问管理网页

这个网站完全不需要申请 SSL 证书(而且 Tailscale 内网申请也很麻烦)。

建立数据库

以下是使用服务器的本地数据库,远程数据库请自行设置。

宝塔建立数据库,名字可以照抄我的 picture-cdn,也可以自己起别的。

还记得之前提到的 scan_config.php 文件吗?宝塔双击打开它,修改:

<?php
# 数据库主机名/ip地址/域名
define('DB_HOST', 'localhost');
# 数据库名
define('DB_NAME', 'picture-cdn');
# 数据库用户名
define('DB_USER', 'picture-cdn');
# 数据库密码
define('DB_PASS', 'password');
# 图片存储网站域名(后面不要加 / )
define('URL_PREFIX', '//pictures.mycdn.com');
# 图片存储网站根目录(后面不要加 / )
define('PATH_PREFIX', '/www/wwwroot/pictures.mycdn.com');

# 格式:['表名', ['文件夹相对于根目录的路径1', '路径2', ...]]
define('CLASS_AND_PATH', [
    ['pixiv', ['/acg/pixiv']],
    ['imas', ['/acg/imas']],
    ['scenery', ['/wallerpapers/Reflectio', '/wallerpapers/Sunny-Sho', '/wallerpapers/china', '/wallerpapers/uwp-windows-jujiao']],
    ['nfs', ['/wallerpapers/nfs18', '/wallerpapers/nfs19']],
    ['games', ['/wallerpapers/games']],
    ['astronomy', ['/wallerpapers/astronomy']]
]);

注意:

  • 域名不必加 https:
  • CLASS_AND_PATH 指的是你想分成哪些图片集,每个图片集包括哪些目录。比如,我的 nfs 图片集包括了 nfs18 nfs19 两个目录。
  • 图片集的目录只需要填相对于相对于根目录的路径。比如,/wallerpapers/nfs19 之后会和 PATH_PREFIX 合并成 /www/wwwroot/pictures.mycdn.com/wallerpapers/nfs19。
  • 图片集的取名也会被作为 SQL 表名,所以名字需要符合 SQL 表名的规范。

扫描图片建立数据库

现在,在安装了 Tailscale 的电脑的浏览器打开你的 pictures-admin.mycdn.com,打开这个非常简陋的网页:

它会检测 scan_config.ini 里设定的图片集。你可以单独更新图片集,也可以更新全部。这里我选择更新全部,经过一段时间的扫描和重建数据库后即可完成:

输出比较长,只截最后一段

以后,只要你的图片有更新,就可以来这里重建数据库。

建立只读数据库用户

刚才给 pictures-admin.mycdn.com 的数据库用户 picture-cdn 对这个数据库有完全的权限,不适合给 pictures-api.mycdn.com 查数据库。为了安全,建立一个只有读权限的数据库用户。

首先用 root 帐户登录 phpmyadmin :

点击 picture-cdn 数据库,点击权限,点击新建账户:

填写用户名 picture-cdn-reader 和密码(待会还会用到,要记住),主机名为本地,取消所有权限,然后执行:

然后我们点击刚创建的帐户的权限

勾上 select,执行

建立 API 网站

宝塔新建网站 pictures-api.mycdn.com ,添加配置:

    location /pics/rpic {
        rewrite ^ /pic/rpic.php last;
    }

    location = /pics/rpicpro {
        rewrite ^ /pic/rpicpro.php last;
    }
    
        #禁止访问目录
    location ^~ /admin/ {
        return 404;
    }
    #禁止访问目录下文件
    location ^~ /admin {
        return 404;
    }
禁止非服务器访问配置文件,以及映射网址

然后建立 adminpic 文件夹:

admin 文件夹放入文件rpic_config.php,并修改用户名和密码为刚才设置的只读账户:

<?php
# 数据库主机名/ip地址/域名
define('DB_HOST', 'localhost');
# 数据库名
define('DB_NAME', 'picture-cdn');
# 数据库用户名
define('DB_USER', 'picture-cdn-reader');
# 数据库密码
define('DB_PASS', 'password');
# pro版api如果没有指定任何参数,默认从这些表中的所有图片随机选取
define('DEFAULT_TABLES', [
    'pixiv',
    'imas'
]);
# 普通版版api如果没有指定任何参数,默认从表中的所有图片随机选取
define('DEFAULT_TABLE', 'pixiv');
?>

pic 文件夹放入文件rpic.phprpicpro.php(这个pro版,如果你不需要这么强大的版本可以不放)。

建议也给这个 API 网站上 SSL。

配置防盗链(可选)

由于随机图 API 暴露在公网上,因此必然有被人白嫖的风险。Http 请求标头会有个 referer 字段,可用于跟踪网站访问来源。对 referer 设置白名单即可阻止不在白名单内的访问。

警告

防盗链虽然可以阻止绝大部分盗链,但是有方法绕过。如果你不想承担被盗刷产生的潜在高额成本支出,请谨记:隐藏自己,不要传播。

首先,在我做的防盗链警示图里下载一张(或者你自定义的防盗链图片),放在 pictures.mycdn.com 网站根目录下,不是白名单里的人访问返回这张图片。

然后打开配置设置防盗链:

  1. 后缀添加 ,webp,以及其他你想包括的格式
  2. 往白名单中加入域名,可以是 *.aaa.com 的泛域名
  3. 填防盗链图片的路径(也可以保留默认的 404 或者 403)
  4. 浏览器直接打开 API 不会传递 referer,如果想浏览器正常访问,就需要允许空 referer。不过,空 referer相对容易伪造。因为我不会没事干天天上浏览器直接打开 API 看图,所以干脆不允许空 referer。
  5. 直接启用。

或者,可以直接修改并保存配置文件:

    location ~ .*\.(jpg|jpeg|gif|png|js|css|webp)$
    {
        expires      30d;
        access_log /dev/null;
        valid_referers *.eterance.com blog.baldcoder.top;
        if ($invalid_referer){
           rewrite /.* /fxxkoff.jpg break;
           # 你也可以直接返回403禁止访问,连防盗图都不给他盗
           #return 403;
        }
    }

现在查看效果。

博客打开图片加载正常,referer 显示是我的博客网址。

但是浏览器直接打开 API 网址,就会显示防盗图或者 403。

403
警告图

也可以在这个网站输入图片的网址检测:https://www.dute.org/httpstatus

只能检测 403 404,如果设置的警示图,虽然这里显示的是 200,但是实际上返回的图是警示图

如何使用以及文档网站

我不打算在这篇博客中花费大量笔墨解释这个 API 如何使用。请参见这个 API 文档网站:

https://eterance.github.io/docusaurus-api-docs/

是的,我连 API 文档都帮你写好了,而且你还能把它搞成你自己的。

源码地址:https://github.com/Eterance/docusaurus-api-docs

如何部署自己的文档网站见:玩转云服务(6):使用 Docusaurus 创建项目文档