一个基于 FastAPI 的现代化写真库浏览系统,支持海报墙展示、缩略图预览、高清原图查看、视频播放和智能搜索。
- 📸 海报墙模式:优雅的文件夹封面展示,支持横屏竖屏自适应
- 🖼️ 高清原图查看:灯箱模式预览,支持键盘导航和预加载
- 🎬 视频支持:自动识别视频文件,提取首帧作为缩略图,支持在线播放
- 🔍 极速搜索:文件名关键词搜索,支持多维度过滤(文件夹/图片/视频)
- ⭐ 收藏夹功能:一键收藏心仪的文件夹、图片和视频,专属收藏页面
- ⚙️ 视图设置:自定义显示列数、缩略图大小等个性化设置
- 🗂️ 多路径支持:同时管理多个照片库目录,跨盘符无缝浏览
- ⚡ 性能优化:智能缓存机制、异步文件读取、缩略图并发控制
- 📱 响应式设计:完美适配桌面、平板和移动设备
- 🔒 安全可靠:路径安全检查,防止越权访问,支持黑名单过滤
- Python: 3.10+
- 核心依赖:
fastapi- Web 框架uvicorn- ASGI 服务器Pillow(PIL) - 图片处理,生成缩略图aiofiles- 异步文件操作python-dotenv- 环境变量管理
- 可选依赖:
opencv-python- 视频缩略图生成(如果启用视频功能)
- 前端框架:
- Vue.js 3 (CDN)
- Tailwind CSS (CDN)
安装所有依赖:
pip install -r requirements.txt仅安装核心依赖(不使用视频功能):
pip install fastapi uvicorn Pillow aiofiles python-dotenvpip install -r requirements.txt创建 .env 文件(可参考 .env.dev.windows):
# 支持单路径
PHOTOS_BASE_DIR=Z:\Pictures\R
# 支持多路径(用分号、逗号或空格分隔)
PHOTOS_BASE_DIR=Z:\Pictures\R;Y:\Pictures\Z
HOST=0.0.0.0
PORT=8000
DEBUG=true
# 可选:启用视频支持(默认启用)
ENABLE_VIDEO=true
# 可选:黑名单路径
BLACKLIST_PATHS=test文件夹,临时文件创建 .env 文件:
# 支持单路径
PHOTOS_BASE_DIR=/home/user/Pictures/R
# 支持多路径
PHOTOS_BASE_DIR=/home/user/Pictures/R,/mnt/nas/Photos
HOST=0.0.0.0
PORT=8000
DEBUG=true
ENABLE_VIDEO=true参考 .env.prod.docker 和 docker-compose.yml 配置。
python app/main.pyuvicorn app.main:app --host 0.0.0.0 --port 8000 --reload打开浏览器访问:http://localhost:8000
- 文件夹浏览:点击文件夹封面进入文件夹,支持面包屑导航
- 图片查看:点击图片打开灯箱模式,支持键盘左右键切换(← →)
- 视频播放:点击视频自动播放,支持播放控制
- 返回上级:点击面包屑或按
ESC键返回
- 快速搜索:点击侧边栏"功能面板",在搜索框输入关键词
- 类型过滤:可选择只搜索文件夹、图片或视频
- 搜索结果:点击结果自动跳转到对应位置
- 搜索范围:自动搜索所有配置的照片库路径
- 添加收藏:点击缩略图右上角的 ⭐ 图标
- 查看收藏:点击侧边栏"我的收藏"或访问
/favorites页面 - 删除收藏:在收藏页面点击删除按钮,或再次点击 ⭐ 图标取消收藏
- 跳转定位:在收藏页面点击项目,自动跳转到原始位置
- 显示列数:调整文件夹和图片的显示列数(1-10 列)
- 缩略图大小:调整缩略图的最大高度(100-800px)
- 设置保存:视图设置自动保存到浏览器本地存储
- 当配置了多个
PHOTOS_BASE_DIR时:- 系统会自动在所有路径中搜索和显示内容
- 搜索结果会包含所有路径下的匹配项
- 收藏夹可以跨路径收藏项目
ESC:关闭灯箱/视频播放器←/→:在灯箱模式下切换上一张/下一张图片点击外部区域:关闭灯箱/侧边栏
- 横屏竖屏自适应:自动适配不同比例的图片,无空白显示
- 每行高度一致:同一行的所有缩略图高度一致,图片垂直居中
- 完整显示:使用
object-contain确保图片完整显示,不裁剪 - 懒加载:只加载可见区域的缩略图,提升加载速度
- 优先级加载:优先加载当前可见的缩略图,快速响应
- 智能缓存:自动缓存目录列表和缩略图,减少重复计算
- 并发控制:限制同时生成的缩略图数量,避免系统过载
- 中断机制:切换目录时自动中断之前的缩略图加载任务
- 流式传输:大文件支持 Range 请求,支持断点续传
编辑 docker-compose.yml,修改 volumes 映射路径:
volumes:
- /your/actual/photos/path:/photos:ro例如(Synology NAS):
volumes:
- /volume1/photo:/photos:rodocker-compose up -d --build- 本地访问:http://localhost:8080
- 局域网访问:http://你的NAS_IP:8080
| 变量名 | 说明 | 默认值 | 示例 |
|---|---|---|---|
PHOTOS_BASE_DIR |
照片库根目录路径(支持多路径) | 自动检测 | Z:\Pictures\R 或 Z:\Pictures\R;Y:\Pictures\Z |
HOST |
服务器绑定地址 | 0.0.0.0 |
0.0.0.0 |
PORT |
服务器端口 | 8000 |
8000 |
DEBUG |
调试模式 | false |
true |
ENABLE_VIDEO |
启用视频支持 | true |
true / false |
THUMBNAIL_SIZE |
缩略图尺寸(宽,高) | 400,600 |
400,600 |
THUMBNAIL_QUALITY |
缩略图质量 | 70 |
70 |
MAX_THUMBNAIL_CONCURRENT |
最大并发缩略图生成数 | 3 |
3 |
ASYNC_FILE_READ |
异步文件读取 | true |
true / false |
FILE_CHUNK_SIZE |
文件读取块大小(字节) | 8192 |
8192 |
BLACKLIST_PATHS |
黑名单路径(多个用分号或逗号分隔) | 无 | 人物A写真集\主题B,test |
- 环境变量(最高优先级)
- .env 文件
- 默认配置
功能说明:
- 支持同时配置多个照片库目录,跨盘符无缝浏览
- 多个路径可以用分号(
;)、逗号(,)或空格()分隔 - 系统会自动在所有配置的路径中搜索和显示内容
配置示例:
# Windows 多路径示例
PHOTOS_BASE_DIR=Z:\Pictures\R;Y:\Pictures\Z
# Linux/Mac 多路径示例
PHOTOS_BASE_DIR=/home/user/Pictures/R,/mnt/nas/Photos
# 带空格的多路径(使用引号包裹单个路径)
PHOTOS_BASE_DIR="Z:\Pictures\R Y:\Pictures\Z"使用场景:
- ✅ 照片库分布在多个硬盘或盘符
- ✅ 需要同时浏览本地和网络存储
- ✅ 统一管理多个不同的照片库
功能说明:
- 黑名单路径中的文件夹和文件将不会被扫描和显示
- 支持相对路径(相对于
PHOTOS_BASE_DIR)和绝对路径 - 多个路径可以用分号(
;)或逗号(,)分隔 - 路径匹配时包含所有子目录和文件(如果文件夹在黑名单中,其所有子内容也会被排除)
配置示例:
# 相对路径示例(相对于 PHOTOS_BASE_DIR)
BLACKLIST_PATHS=人物A写真集\主题B,人物B写真集\test文件夹
# 绝对路径示例
BLACKLIST_PATHS=Z:\Pictures\R\人物A写真集\主题B;Z:\Pictures\R\test
# 混合使用
BLACKLIST_PATHS=人物A写真集\主题B,/photos/test文件夹,绝对路径\文件夹使用场景:
- ✅ 排除测试文件夹或临时文件夹
- ✅ 排除不想公开的私人文件夹
- ✅ 排除正在整理或处理的文件夹
- ✅ 排除包含大量文件的系统文件夹
注意事项:
- 路径使用反斜杠(
\)或正斜杠(/)都可以 - Windows 路径可以使用反斜杠,Linux/Mac 使用正斜杠
- 修改黑名单后需要重启应用才能生效
在 Windows 上,如果未设置 PHOTOS_BASE_DIR,系统会:
- 尝试使用默认路径
Z:\Pictures\R和Y:\Pictures\Z - 如果都不存在,使用项目目录下的
photos文件夹
缩略图生成性能:
MAX_THUMBNAIL_CONCURRENT:控制同时生成的缩略图数量(默认 3)- 增加此值可加快缩略图生成,但会消耗更多 CPU 和内存
- 建议根据服务器性能调整(2-5 之间)
文件读取性能:
ASYNC_FILE_READ:启用异步文件读取(默认启用)- 对大文件和高并发场景有明显性能提升
FILE_CHUNK_SIZE:文件读取块大小(默认 8192 字节)- 对于大视频文件,可以适当增加此值(如 32768)
缓存配置:
- 目录列表缓存:5 分钟(内存缓存,自动清理)
- 缩略图缓存:24 小时(文件缓存,存储在
.cache/thumbnails) - 收藏夹缓存:30 秒(内存缓存,减少文件IO)
在 Docker 容器中:
- 自动检测容器环境(通过
/.dockerenv) - 默认使用
/photos作为 BASE_DIR - 通过
docker-compose.yml的 volumes 映射实际路径
系统会自动识别以下格式的图片文件(不区分大小写):
.jpg/.jpeg.png.webp.gif(包括动画 GIF,显示时会取第一帧作为缩略图)
系统支持以下视频格式(需要在配置中启用 ENABLE_VIDEO=true):
.mp4/.m4v.mov.avi.mkv.webm.flv.wmv
视频功能说明:
- 自动提取视频第一帧作为缩略图
- 支持在线播放(使用浏览器原生播放器)
- 视频缩略图生成需要安装
opencv-python(已在 requirements.txt 中包含) - 可以通过
ENABLE_VIDEO=false禁用视频功能
你可以灵活组织你的照片库目录结构,系统支持任意层级的文件夹嵌套:
Z:\Pictures\R\ # BASE_DIR(根目录)
├── 人物A写真集\
│ └── 主题A
│ │ ├── 图片1.jpg
│ │ ├── 图片2.png
│ │ └── 子文件夹\
│ │ ├── 图片3.webp
│ │ ├── 视频1.MOV
│ │ └── 视频2.MP4
│ └── 主题B
│ ├── 图片1.jpg
│ ├── 图片2.png
│ └── 子文件夹\
│ └── 图片3.webp
├── 人物B写真集\
│ └── 图片4.jpg
关键要点:
- ✅ 所有图片和文件夹必须放在
PHOTOS_BASE_DIR目录下(或子目录中) - ✅ 支持任意深度的文件夹嵌套
- ✅ 图片可以直接放在根目录,也可以放在任意子文件夹中
- ✅ 文件夹会自动显示,点击可进入查看内容
系统会自动处理:
- ✅ 文件名支持中文、英文、数字和常用符号
- ✅ 不区分大小写(
.JPG和.jpg都会被识别) - ✅ 文件名中可以包含空格
会被跳过的内容:
- ❌ 隐藏文件和文件夹(以
.开头的,如.DS_Store、.hidden) - ❌ 非支持格式的文件(如
.txt、.pdf等,除非是图片或视频格式) - ❌ 视频文件(如果
ENABLE_VIDEO=false时会被跳过) - ❌ 系统没有访问权限的文件或文件夹
- ❌ 黑名单路径中的文件夹和文件(通过
BLACKLIST_PATHS配置) - ❌ 空文件夹或只包含不支持格式文件的文件夹
系统会自动为每个文件夹生成封面图(优化版本):
-
智能查找策略(按优先级):
- 策略 1:优先查找当前文件夹直接包含的图片或视频
- 策略 2:如果当前文件夹没有,查找直接子文件夹中的第一张图片/视频
- 策略 3:如果以上都没有,递归查找深层文件夹中的第一张图片/视频
- 按照文件名的字母顺序查找
- 支持图片和视频作为封面(仅在启用视频功能时包含视频)
-
封面缓存:
- 封面查找结果会被缓存,避免重复计算
- 缓存大小限制为 1000 个条目,自动清理
-
建议:
- 在文件夹的根目录放置一张代表性图片作为封面(命名靠前,如
封面.jpg或01-封面.jpg) - 或者在子文件夹的第一层放置图片,系统会自动查找
- 文件名可以使用数字前缀来控制封面选择顺序
- 如果文件夹只包含视频且启用视频功能,会自动使用视频第一帧作为封面
- 在文件夹的根目录放置一张代表性图片作为封面(命名靠前,如
- 实时扫描: 每次浏览目录时都会实时扫描文件系统,显示最新内容
- 缓存机制:
- 目录列表缓存 5 分钟(内存缓存)
- 缩略图缓存 24 小时(文件缓存)
- 封面图片缓存(避免重复计算)
- 收藏夹数据缓存 30 秒(减少文件IO)
- 排序规则: 文件夹和媒体文件按名称字母顺序排列
- 递归搜索: 文件夹封面会智能搜索:优先当前目录 → 直接子目录 → 深层递归
- 性能优化:
- 异步文件读取,支持大文件流式传输
- 缩略图生成并发控制(默认最多 3 个并发)
- 智能缓存清理,自动管理内存和磁盘空间
-
保持目录整洁:
推荐结构: Z:\Pictures\R\ ├── 2024年\ │ ├── 写真集1\ │ │ ├── 封面.jpg # 命名靠前,优先作为封面 │ │ ├── 001.jpg │ │ └── 002.jpg │ └── 写真集2\ └── 2025年\ └── 写真集3\ -
命名规范:
- 使用有意义的文件夹名称(如主题、日期等)
- 图片文件可以使用编号前缀(
001.jpg,002.jpg)保证顺序 - 封面图片可以使用
封面.jpg或01-封面.jpg等名称,确保优先显示
-
避免的问题:
- 不要将图片放在隐藏文件夹中(以
.开头) - 不要在文件夹名称中使用特殊符号(如
<,>,:,",|,?,*) - 确保图片文件完整,避免损坏的图片文件
- 不要将图片放在隐藏文件夹中(以
-
性能优化建议:
- 单个文件夹内不建议放置过多文件(建议 < 1000 个)
- 使用合理的文件夹层级(建议不超过 5 层)
- 定期清理
.cache/thumbnails目录释放磁盘空间(可选) - 使用黑名单功能排除不需要扫描的目录
-
多路径管理:
- 将不同类型的照片库分开存储(如:写真库、视频库)
- 使用有意义的盘符或路径名称便于识别
- 注意路径访问权限,确保所有路径都可读
-
收藏夹使用:
- 收藏常用的文件夹,快速访问
- 收藏精美的图片或视频,便于回顾
- 定期整理收藏夹,保持整洁
PortraitVault/
├── app/
│ ├── main.py # 主应用文件(API 路由和业务逻辑)
│ ├── config.py # 配置管理模块(支持多路径、黑名单等)
│ ├── cache.py # 缓存管理模块(内存缓存和文件缓存)
│ └── static/
│ ├── index.html # 主页面(浏览、搜索、视图设置)
│ ├── favorites.html # 收藏夹页面
│ ├── favicon.ico # 网站图标
│ └── 404.png # 占位符图片
├── .cache/
│ └── thumbnails/ # 缩略图缓存目录(自动生成)
├── favorites.json # 收藏夹数据文件(自动生成)
├── docker-compose.yml # Docker Compose 配置
├── Dockerfile # Docker 镜像构建文件
├── requirements.txt # Python 依赖
├── .env.example # 环境变量配置示例
├── env.dev.windows # Windows 开发环境示例
├── env.prod.docker # Docker 生产环境示例
└── README.md # 本文件
GET /- 主页面(照片库浏览)GET /favorites- 收藏夹页面GET /favicon.ico- 网站图标
GET /api/browse?path=- 浏览目录内容(支持多路径)GET /api/thumbnail?path=- 获取缩略图(图片/视频/文件夹封面)GET /api/raw?path=- 获取高清原图或视频(支持 Range 请求)
GET /api/search?keyword=...&file_type=...&limit=...- 搜索文件(支持文件名关键词、类型过滤)GET /api/favorites- 获取所有收藏项POST /api/favorites- 添加收藏项(Body:{path, name, type})DELETE /api/favorites?path=...- 删除收藏项
搜索功能说明:
- 支持文件名关键词搜索(不区分大小写)
- 支持类型过滤:
folder(文件夹)、image(图片)、video(视频) - 支持结果数量限制(默认 100,最大 1000)
- 搜索范围包括所有配置的
PHOTOS_BASE_DIR - 搜索结果实时返回,毫秒级响应
收藏夹功能说明:
- 支持收藏文件夹、图片和视频
- 收藏数据持久化存储在
favorites.json - 提供专门的收藏页面进行管理
- 支持一键跳转到原始位置
- ✅ 路径安全检查:防止访问 BASE_DIR 以外的目录,防止路径遍历攻击
- ✅ 只读挂载:Docker 部署建议使用
:ro(只读)挂载,保护原文件不被修改 - ✅ 隐藏文件过滤:自动跳过以
.开头的隐藏文件和文件夹 - ✅ 黑名单机制:支持配置黑名单路径,排除不需要访问的目录
- ✅ 多路径验证:所有路径都经过安全验证,确保在允许范围内
- ✅ 输入验证:所有 API 输入都经过验证和清理,防止注入攻击
解决方案:
- 检查
.env文件中的PHOTOS_BASE_DIR路径是否正确 - 在 Windows 上,确保使用正确的路径格式(如
Z:\Pictures\R) - 在 Linux 上,确保路径存在且有访问权限
解决方案:
- 检查
docker-compose.yml中的 volumes 映射是否正确 - 确保 NAS 上的路径存在且容器有访问权限
- 检查 Docker 的共享文件夹设置
解决方案:
- 确保 Pillow 已正确安装:
pip install Pillow - 检查图片文件是否损坏
- 查看服务器日志获取详细错误信息
可能原因及解决方案:
-
图片格式不支持
- ✅ 确保图片格式为
.jpg,.jpeg,.png,.webp,.gif之一 - ❌ 其他格式(如
.bmp,.tiff,.ico等)不会被扫描 - 💡 注意:动画 GIF 在缩略图中会显示第一帧,点击查看原图可以看到完整动画效果
- ✅ 确保图片格式为
-
文件在隐藏文件夹中
- 检查文件夹名称是否以
.开头(如.hidden) - 隐藏文件和文件夹会被自动跳过
- 检查文件夹名称是否以
-
文件路径超出 BASE_DIR
- 确保所有图片都在
PHOTOS_BASE_DIR目录下 - 检查
.env文件中的路径配置是否正确
- 确保所有图片都在
-
权限问题
- Windows:确保运行 Python 的用户有读取照片目录的权限
- Linux/Mac:检查文件权限(
chmod)和所有者
-
文件损坏
- 尝试用其他软件打开图片,确认文件完整
- 损坏的图片可能无法生成缩略图
-
缓存问题
- 如果修改了文件但界面没更新,等待 5 分钟后自动刷新
- 或清除浏览器缓存后重新加载
解决方案:
- 确保文件夹内至少有一张支持的图片格式(或视频文件,如果启用视频功能)
- 检查子文件夹中是否有图片(系统会递归搜索)
- 如果文件夹为空,会显示默认占位符(这是正常现象)
- 等待缓存过期(24小时)后,封面会自动更新
解决方案:
- 确保已安装
opencv-python:pip install opencv-python - 检查配置中
ENABLE_VIDEO=true(默认已启用) - 确认视频文件格式在支持列表中
- 检查视频文件是否损坏(尝试用其他播放器打开)
- 视频缩略图生成可能需要较长时间,请耐心等待
解决方案:
- 确保搜索关键词不为空
- 检查是否选择了正确的文件类型过滤
- 搜索结果限制在 1000 条以内,如果结果太多,尝试更具体的关键词
- 搜索会在所有配置的
PHOTOS_BASE_DIR中进行
解决方案:
- 检查路径分隔符是否正确(支持
;、,或空格) - 确保所有路径都存在且可访问
- Windows 路径可以使用反斜杠或正斜杠
- 修改配置后需要重启应用
- 查看启动日志确认所有路径都已加载
解决方案:
- 检查
favorites.json文件是否存在且可读写 - 确保应用有写入权限
- 收藏数据存储在项目根目录下的
favorites.json - 可以手动备份该文件防止数据丢失
- ✅ 多路径支持:支持同时配置多个照片库目录,跨盘符无缝浏览
- ✅ 视频支持:自动识别视频文件,提取首帧作为缩略图,支持在线播放
- ✅ 极速搜索:文件名关键词搜索,支持多维度过滤,毫秒级响应
- ✅ 收藏夹功能:一键收藏,专属收藏页面,支持快速访问
- ✅ 智能缓存:多层次缓存机制,自动清理过期缓存
- ✅ 异步处理:异步文件读取,支持大文件流式传输
- ✅ 并发控制:缩略图生成并发控制,避免系统过载
- ✅ 懒加载:缩略图懒加载,优先加载可见区域
- ✅ 横屏竖屏自适应:自动适配不同比例的图片,无空白显示
- ✅ 视图设置:自定义显示列数和缩略图大小
- ✅ 响应式设计:完美适配各种设备
- ✅ 键盘快捷键:支持键盘导航,提升操作效率
- ✅ 代码精简:提取通用函数,减少重复代码
- ✅ 性能优化:优化文件扫描和搜索算法
- ✅ 稳定性增强:统一错误处理,原子性文件操作
- ✅ 可维护性:优化代码结构,提升可读性
- 后端框架:FastAPI(异步、高性能)
- 前端框架:Vue.js 3(组合式 API)
- 样式框架:Tailwind CSS(响应式、现代化)
- 图片处理:Pillow(缩略图生成)
- 视频处理:OpenCV(视频缩略图提取)
- 缓存策略:内存缓存 + 文件缓存
- 部署方式:Docker 容器化部署
MIT License
欢迎提交 Issue 和 Pull Request!
感谢所有使用和支持 PortraitVault 的用户!