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.
指代,解析到服务器公网 IP。mycdn
.com
这三个域名可以都是同一个二级域名衍生的三级域名。
下面是我在 Cloudflare 的域名解析。
建立图片网站
下面使用宝塔。
图片存储网站
首先建立网站 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;
}
这个网站完全不需要申请 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;
}
然后建立 admin
和 pic
文件夹:
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.php和rpicpro.php(这个pro版,如果你不需要这么强大的版本可以不放)。
建议也给这个 API 网站上 SSL。
配置防盗链(可选)
由于随机图 API 暴露在公网上,因此必然有被人白嫖的风险。Http 请求标头会有个 referer
字段,可用于跟踪网站访问来源。对 referer
设置白名单即可阻止不在白名单内的访问。
警告
防盗链虽然可以阻止绝大部分盗链,但是有方法绕过。如果你不想承担被盗刷产生的潜在高额成本支出,请谨记:隐藏自己,不要传播。
首先,在我做的防盗链警示图里下载一张(或者你自定义的防盗链图片),放在 pictures.mycdn.com
网站根目录下,不是白名单里的人访问返回这张图片。
然后打开配置设置防盗链:
- 后缀添加
,webp
,以及其他你想包括的格式 - 往白名单中加入域名,可以是
*.aaa.com
的泛域名 - 填防盗链图片的路径(也可以保留默认的 404 或者 403)
- 浏览器直接打开 API 不会传递 referer,如果想浏览器正常访问,就需要允许空 referer。不过,空 referer相对容易伪造。因为我不会没事干天天上浏览器直接打开 API 看图,所以干脆不允许空 referer。
- 直接启用。
或者,可以直接修改并保存配置文件:
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。
也可以在这个网站输入图片的网址检测:https://www.dute.org/httpstatus
如何使用以及文档网站
我不打算在这篇博客中花费大量笔墨解释这个 API 如何使用。请参见这个 API 文档网站:
https://eterance.github.io/docusaurus-api-docs/
是的,我连 API 文档都帮你写好了,而且你还能把它搞成你自己的。
源码地址:https://github.com/Eterance/docusaurus-api-docs
如何部署自己的文档网站见:玩转云服务(6):使用 Docusaurus 创建项目文档
Comments 11 条评论
博主 1113832440
你好,我搭建的检索的时候图片集里的图片超过2500张图片了就出现这个
Warning: Trying to access array offset on value of type bool in /www/wwwroot/1.liufu.fun/admin/scan.php on line 166
Warning: Trying to access array offset on value of type bool in /www/wwwroot/1.liufu.fun/admin/scan.php on line 167
Fatal error: Uncaught DivisionByZeroError: Division by zero in /www/wwwroot/1.liufu.fun/admin/scan.php:168 Stack trace: #0 {main} thrown in /www/wwwroot/1.liufu.fun/admin/scan.php on line 168
是什么问题吗?谢谢
博主 Eterance
@1113832440 看了一下代码,Fatal error 里的 scan.php 168 行是计算图片宽高比的,可能是你的某张图片有问题,读取到的高是 0 导致了除于 0 的错误。你可以尝试一下用二分法找出这张图片,就是每次取一半图片,不断取一半缩小嫌疑范围
博主 Eterance
@1113832440 我修改了一下 github 上的 scan.php 代码,应该可以出现错误提示当前出错的照片名字,你更新一下代码文件
博主 1113832440
@Eterance 好的,谢谢
博主 1113832440
@Eterance 还是一样,没提示
Warning: getimagesize(): Corrupt JPEG data: 6946 extraneous bytes before marker in /www/wwwroot/1.liufu.fun/admin/scan.php on line 164
有些是显示这个,还是没出现文件名
博主 Eterance
@1113832440 失误,没有正确捕捉到错误。我重新修改了代码并且测试了一下,这次应该能正确捕捉错误并且显示文件名了
博主 1113832440
@Eterance 谢谢。我试试
博主 1113832440
@Eterance 我的图片已经用二分法全部排除了,有问题的图片打开也是没问题的
博主 Eterance
@1113832440 你的意思是指,你找到了引发错误的图片,但是用图片查看器打开图片是没问题的吗?你去除了这些图片后再扫描图片,还会不会有其他错误?以及如果你不介意的话,能否把引发错误的图片发到邮箱blog@eterance.com让我看看怎么回事吗?
博主 1113832440
是的,检索错误的图片我下载下来了,是可以打开的图片也是有信息的,图片我删了,后面我再看有没有这样的,有了我发给你看看,后面更新了好像还是不能显示错误的文件名,你是检索界面那里直接显示出来吗?
博主 Eterance
@1113832440 是的,理论上应该检索界面直接显示错误的图片文件名