一个基于 FastAPI + Fabric.js 的单机图片画廊与编辑器,支持目录树浏览、在线编辑(马赛克涂抹、裁剪、缩放、旋转、翻转)、保存到自定义路径等功能。
| 层 | 技术 |
|---|---|
| 后端框架 | Python 3 + FastAPI |
| 图片处理 | Pillow |
| 前端 | 纯 HTML / CSS / JavaScript(无构建工具) |
| 图片编辑器 | Fabric.js v5.3(CDN) |
| 样式 | Tailwind CSS(CDN) |
| 运行时 | uvicorn |
- 可配置图片根目录:通过 Web 界面设置服务器上的任意文件夹作为图片源
- 可配置保存目录:编辑后的图片保存到独立的自定义路径
- 目录树浏览:可折叠的懒加载目录树,支持嵌套子文件夹
- 图片预览:点击文件条目弹出模态框,显示大图
- 一键编辑:预览窗中点击"编辑图片"在新标签页打开编辑器
| 工具 | 说明 |
|---|---|
| 🔲 马赛克笔刷 | 按住鼠标涂抹,对局部区域进行像素化处理 |
| ✂️ 裁剪 | 拖拽蓝色虚线框 + 边角缩放 → 确认裁剪 |
| 📐 缩放 | 输入百分比等比缩放(50 = 缩小一半) |
| ↺↻ 旋转 | 左转 / 右转 90° |
| ↔↕ 翻转 | 水平镜像 / 垂直镜像 |
| ↩↪ 撤销/重做 | 支持 Ctrl+Z / Ctrl+Y,最多 40 步 |
| 💾 保存 | 按日期(yyyy-mm-dd/文件名)保存到配置的保存目录 |
| ⬇ 下载 | 浏览器下载,文件名含日期前缀 |
- 自动生成缩略图并缓存(
thumbnails/目录,保持子目录结构) - 保存时自动去重(已存在文件追加
_(1)_(2)) - 路径穿越安全防护(拒绝
..+os.path.commonpath越权检查) - 50MB 请求体限制
- CORS 全开放(便于本地使用)
gallery_app/
├── main.py # FastAPI 后端主程序
├── requirements.txt # Python 依赖
├── config.json # 配置文件(自动生成)
├── README.md # 本文件
├── static/
│ ├── index.html # 画廊首页(目录树浏览)
│ └── editor.html # Fabric.js 图片编辑器
├── images/ # [可选] 默认图片目录
├── edited/ # 默认保存目录
├── thumbnails/ # 缩略图缓存
└── backups/ # 历史版本备份
- Python 3.8+
- pip
cd gallery_app
pip install -r requirements.txtrequirements.txt 内容:
fastapi
uvicorn
pillow
python-multipart
aiofiles
# 方式 A:直接运行(默认端口 65530)
python main.py
# 方式 B:指定端口
PORT=8000 python main.py
# 方式 C:使用 uvicorn 命令行
uvicorn main:app --host 0.0.0.0 --port 65530http://0.0.0.0:65530/static/index.html
- 页面顶部会提示"未设置图片根目录"
- 输入你的图片文件夹路径(如
/home/user/Pictures) - 输入编辑结果保存路径(如
/home/user/outputs,不能与图片根目录相同) - 点击"保存"
- 目录树自动加载
config.json 在首次保存配置时自动生成,格式:
{
"root_path": "/home/user/Pictures",
"saved_root_path": "/home/user/outputs"
}| 字段 | 说明 | 默认值 |
|---|---|---|
root_path |
图片浏览根目录(/api/browse 扫描此目录) |
null(未配置) |
saved_root_path |
编辑结果保存目录(/api/save 写入此目录) |
./edited/ |
⚠️ 重要:saved_root_path不能与root_path相同,后端会返回 400 错误。
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/config |
获取当前配置 {root_path, saved_root_path} |
POST |
/api/config |
更新配置 {"root_path": "...", "saved_root_path": "..."} |
| 方法 | 路径 | 参数 | 说明 |
|---|---|---|---|
GET |
/api/browse |
?path=(相对路径,空=根目录) |
返回 [{name, type, path, thumbnail_url}] |
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/thumbnail?path= |
返回缩略图(自动生成 JPEG,最大 400px,缓存于 thumbnails/) |
GET |
/api/image/{path:path} |
返回原图 |
| 方法 | 路径 | 请求体 | 响应 |
|---|---|---|---|
POST |
/api/save |
{"image_data": "base64...", "relative_path": "2026-05-02/文件名.png"} |
{"status":"success", "absolute_path":"/...", "relative_path":"..."} |
保存规则:
- 图片写入
saved_root_path/relative_path - 父目录自动递归创建
- 文件名冲突时自动追加
_(1)_(2) - 路径穿越检测(拒绝
..)
- 在画廊首页点击任意图片条目 → 弹出预览窗 → 点击"编辑图片"
- 编辑器在新标签页打开,图片自动适应画布大小
- 默认模式下,可拖拽移动图片查看不同区域
- 点击工具栏 🔲 马赛克 按钮(变紫色高亮)
- 调整笔刷直径滑块(8–80px)和马赛克块大小(4–40px)
- 在图片上按住鼠标拖动涂抹需要遮盖的区域
- 每次松开鼠标自动保存撤销点(支持 Ctrl+Z 回退)
- 再次点击马赛克按钮或按 ESC 退出
- 点击 ✂️ 裁剪 → 出现蓝色虚线矩形框
- 拖拽矩形移动位置,拖拽边角圆点调整大小
- 点击 ✓ 确认 执行裁剪 / ✕ 取消 退出
- 在底部文件名输入框中可修改文件名
- 点击 💾 保存到服务器 → 弹出模态框显示保存路径预览 → 确认
- 文件保存到
saved_root_path/yyyy-mm-dd/文件名.png - 点击 ⬇ 下载 直接浏览器下载
-
目录权限:确保运行
main.py的用户对root_path有读取权限,对saved_root_path有读写权限。 -
首次配置:未设置
root_path时,画廊页面显示输入框;未设置saved_root_path时,编辑器顶部显示红色警告条并禁用保存按钮。 -
保存路径隔离:
saved_root_path不能与root_path设为同一目录,避免循环引用。 -
缩略图缓存:缩略图保存在项目
thumbnails/下,保持与源目录相同的子目录结构。如果源图片更新(修改时间变化),缩略图会自动重新生成。 -
支持格式:
.jpg.jpeg.png.webp.bmp.gif -
文件大小限制:请求体最大 50MB,超出返回 413。
-
端口占用:默认端口
65530,可通过环境变量PORT修改。 -
跨域:默认允许所有来源(CORS
*),仅适合本地/内网使用。如需限制,修改main.py中的allow_origins。 -
Fabric.js 撤销机制:撤销基于画布快照(每次操作前保存),不依赖 Fabric 内置状态管理。快照栈上限 40 步。
-
浏览器兼容:推荐使用最新版 Chrome / Edge / Firefox。
A: 确认访问路径为 /static/index.html(而非根路径 /),且端口号与启动时一致。
A: 在画廊首页(index.html)点击"修改"按钮,设置 saved_root_path。确保该目录存在且有写入权限。
A: 文件保存到 saved_root_path/yyyy-mm-dd/文件名.png。检查服务器控制台日志([保存请求] 前缀的 print 输出)和浏览器控制台(F12)的 [保存] 日志。
A: 确认"马赛克"按钮已激活(紫色高亮)。如仍有问题,刷新页面重试。
A: 画廊首页顶部点击"修改"按钮,在弹出的输入框中修改 图片根目录 路径,保存后目录树自动刷新。
A: 检查 thumbnails/ 目录是否有写入权限。后端会在首次访问时自动生成缩略图。
A: 可以,指定不同端口即可:PORT=8001 python main.py