随机图片 API
之前我也做过一个随机图片 API,不过随着文件越来越多,维护起来逐渐变得麻烦。
所以这次干脆重新设计了一套。
这就是现在的 random-image-api项目。
它的目标其实很简单:
从 S3 中管理图片和视频,然后提供一个简单的 /random API,随机返回一个符合条件的媒体文件。
项目简介
random-image-api 是一个基于 PHP + S3 兼容对象存储 + 数据库索引 的随机媒体 API。
媒体文件保存在 S3、Cloudflare R2、MinIO 等对象存储中,数据库只保存对象索引。API 请求时直接从数据库中随机选择符合条件的媒体,然后返回对象路径、URL,或者通过 HTTP 302 跳转到实际文件。
整体架构如下:
S3 / R2 / MinIO
│
│ ListObjectsV2
▼
sync.php
│
│ 建立索引
▼
SQLite / MySQL / PostgreSQL
│
│ 随机查询
▼
/random API
│
▼
CDN / S3
准备项目
首先将项目上传到服务器:
git clone https://github.com/CleverBSOD/random-image-api.git
cd random-image-api
复制环境变量配置:
cp .env.example .env
chmod 600 .env
然后根据自己的对象存储修改 .env。
配置 S3
最重要的几个配置:
S3_ENDPOINT=https://s3.example.com
S3_BUCKET=images
S3_REGION=us-east-1
S3_ACCESS_KEY=
S3_SECRET_KEY=
如果使用 Cloudflare R2,例如:
S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
S3_BUCKET=images
S3_REGION=auto
S3_ADDRESSING_STYLE=virtual
MinIO 则可以:
S3_ENDPOINT=https://minio.example.com
S3_BUCKET=images
S3_REGION=us-east-1
S3_ADDRESSING_STYLE=path
项目支持 path 和 virtual 两种 S3 寻址方式。
如果只使用公开 Bucket,可以不配置 Access Key。
如果 Bucket 是私有的,则需要配置:
S3_ACCESS_KEY=your_access_key
S3_SECRET_KEY=your_secret_key
程序会在需要时生成 Presigned URL。
限制同步目录
如果整个 Bucket 里还有其他文件,建议配置:
S3_PREFIX=wallpapers
这样同步时只处理:
wallpapers/
下面的对象。
例如:
wallpapers/
├── anime/
├── nature/
└── game/
这样不仅可以减少同步时间,也可以避免把无关对象加入随机索引。
首次同步
配置完成后,执行:
php sync.php
同步程序会:
- 调用 S3
ListObjectsV2 - 找到对象
- 判断是图片还是视频
- 对图片识别横图、竖图或正方形
- 将对象写入数据库
- 清理已经不存在的旧索引
同步完成后会输出类似:
同步完成:索引 1234 个对象,移除 12 条过期记录。
图片方向识别只会读取对象开头的一部分数据,而不是下载整个文件。当前实现读取前 256 KiB,并使用 getimagesizefromstring() 判断尺寸。
Docker 部署
项目也提供了 Docker Compose 部署方式。推荐在1Panel上部署,非常方便。
整体包含:
web
sync
init-db
其中:
web提供 HTTP APIsync负责对象存储同步init-db初始化数据库
媒体文件仍然保存在 S3 / R2 / MinIO,Docker 卷只保存数据库索引。
在 1Panel 中可以:
容器
↓
编排
↓
创建编排
选择项目目录后使用项目中的 docker-compose.yml。
默认 Web 服务绑定:
127.0.0.1:8080
如果 8080 已经被占用,可以修改:
HTTP_PORT=8080
配置域名和 HTTPS
在 1Panel 中创建网站,例如:
random.example.com
然后配置反向代理:
http://127.0.0.1:8080
最后申请 HTTPS 证书。
测试:
curl https://random.example.com/healthz
如果返回正常,说明 Web 服务已经运行。
然后测试随机 API:
curl 'https://random.example.com/random?type=url&form=text'
或者:
curl -L \
'https://random.example.com/random?type=img&orientation=landscape' \
-o random-image.jpg
API 快速使用
最简单:
GET /random
默认返回随机图片。
JSON:
curl 'https://example.com/random'
返回:
{
"url": "/wallpaper/example.jpg"
}
返回纯文本:
curl 'https://example.com/random?form=text'
直接返回完整 URL:
curl 'https://example.com/random?type=url&form=text'
直接作为图片地址:
<img
src="https://example.com/random?type=img"
alt="随机图片"
>
FAQ
Q:为什么不直接每次扫描 S3?
API 请求本身只需要“随机选择一个符合条件的对象”,没必要每次都扫描整个 Bucket。
项目采用数据库索引,将对象存储的扫描操作放到后台同步任务中,API 请求只查询索引。
Q:为什么要数据库?
数据库保存对象的:
object_key
content
orientation
random_token
这样 API 就可以直接进行:
目录筛选
媒体类型筛选
方向筛选
随机选择
而不用实时操作对象存储,保证性能。
Q:SQLite 能不能用?
可以。SQLite是默认方案,同时也是最省事的方案。
项目同时支持 MySQL 和 PostgreSQL,因此后续需要迁移时也有选择。
Q:为什么图片没有经过 PHP?
PHP 只负责:
找到文件
↓
生成 URL
↓
返回 / 302
真正的图片或视频由 S3、R2、MinIO 或 CDN 负责传输。
这样可以避免 PHP Web 服务承担大量媒体流量。
Q:新上传的图片为什么没有立即出现?
因为数据库索引不是实时同步的。
Q:私有 Bucket 可以用吗?
配置:
S3_ACCESS_KEY=...
S3_SECRET_KEY=...
在没有配置公共 URL 的情况下,程序会为对象生成临时签名 URL。
Q:支持哪些对象存储?
项目使用 S3 兼容 API,因此可以用于 AWS S3、Cloudflare R2、MinIO,以及其他实现兼容 S3 接口的对象存储。
Q:如何重新识别所有图片方向?
php sync.php --refresh-orientation
这会强制重新读取图片数据并更新方向索引。
Q:为什么 /random?orientation=portrait 返回 404?
手动指定方向时,项目采用严格匹配。
如果当前目录下没有符合条件的图片,就返回:
404
而:
orientation=auto
在没有匹配方向时会降级到任意方向。
Q:如何只随机某个目录?
使用:
/random?dir=wallpaper
也可以继续指定子目录:
/random?dir=wallpaper/anime
dir 会被限制在配置好的 S3_PREFIX 下,并禁止 .. 路径穿越。
十三、总结
random-image-api 的核心架构:
对象存储
负责存文件
数据库
负责索引
sync.php
负责同步
random.php
负责 API
CDN / S3
负责真正传输文件
项目地址:
https://github.com/CleverBSOD/random-image-api