十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

LibrePhotos 2025 年 7—8 月开发进展:单容器统一部署、相册公开链接分享与文件夹导航

LibrePhotos 2025 年 7—8 月开发进展:单容器统一部署、相册公开链接分享与文件夹导航 LibrePhotos 2025 年 7—8 月开发进展单容器统一部署、相册公开链接分享与文件夹导航【免费下载链接】librephotosA self-hosted open source photo management service.项目地址: https://gitcode.com/GitHub_Trending/li/librephotosLibrePhotos 是一个自托管的开源照片管理服务2025 年第 35 周发布的开发日志2025-08-28-2025w35.md集中展示了 7—8 月间三个重量级新特性将 API 与前端合并进单个容器的 librephotos-unified 统一镜像、通过链接对外公开分享相册、以及带分页和子文件夹照片计数的文件夹导航视图。本文以该日志为骨架结合仓库中的 Dockerfile、settings 模块、ViewSet 与模型源码逐项展开帮助你了解这些特性的实现原理、正确配置方式与适用前提。一、单容器统一部署librephotos-unified 镜像此前 LibrePhotos 的标准部署方式是后端 前端 nginx 反代的拆分架构。7—8 月的一大进展是Docker 现在支持用librephotos-unified:latest单个容器启动整个 LibrePhotos由 Django 直接同时承担 API 与 React 前端静态文件的对外服务从而完全去掉 nginx 代理这一层。1.1 工作原理统一镜像的实现思路记录在 deploy/docker/unified/README.md其核心是在构建镜像的多阶段过程中先用node:20-slim构建 React 前端yarn run build产物位于/frontend/dist后端阶段将前端构建产物复制到/code/frontend_build并通过 deploy/docker/unified/Dockerfile 安装whitenoise作为静态文件服务中间件Django 用 WhiteNoise 高效地直接对外服务前端静态资源URL 路由约定为/api/*交给 Django API其余路径交给 React 前端SPA catch-all由于 API 与前端同源CORS 配置被大幅简化。1.2 无代理的 Django 设置模块统一镜像对应专门的设置模块 production_noproxy.py。该模块通过from .production import *继承生产配置只覆盖去掉代理后必须改变的部分SERVE_FRONTEND True强制开启前端服务librephotos/urls.py依据它挂载 SPA catch-all 路由api/views/views.py依据它决定/是否仍属于 DRF 的 API 根路径静态文件STATICFILES_DIRS指向/code/frontend_build存储后端采用whitenoise.storage.CompressedManifestStaticFilesStorage中间件紧跟在SecurityMiddleware之后插入whitenoise.middleware.WhiteNoiseMiddleware主机与 CORSALLOWED_HOSTS [*]、CORS_ALLOW_ALL_ORIGINS True因为容器前没有任何重写 Host 头的组件且前后端同源后 CORS 不再构成实际限制CSRF从CSRF_TRUSTED_ORIGINS环境变量逗号分隔读取运维者自己的域名数据库新增DB_BACKEND环境变量默认sqlite也可选postgresql见下节。该模块还经历了一次重要的架构修正早期版本是一份从生产配置复制出来的独立文件由 entrypoint 在启动时覆盖真实设置结果导致新增的MAP_TILE_PROVIDER、OCR_MODEL等CONSTANCE_CONFIG配置键永远到不了这份副本GET /api/sitesettings直接返回 500、整个 UI 无法加载。改为继承覆盖后这类复制漂移问题从根上被消除。这是理解该模块设计的关键背景也是它写成import production 后只改差异的根本原因。1.3 快速启动方式方式一Docker Compose推荐使用仓库中的无代理编排文件docker-compose -f docker-compose.no-proxy.yml up -d该文件位于 deploy/docker/unified/docker-compose.no-proxy.yml包含一个 PostgreSQL 数据库服务pgautoupgrade/pgautoupgrade带健康检查和一个 backend 服务backend 直接使用reallibrephotos/librephotos-unified:${tag}镜像端口映射${httpPort:-3000}:8001且已在环境变量中预设SERVE_FRONTENDtrue。方式二直接 docker runSQLite 单机模式# 创建数据目录结构 mkdir -p ./librephotos-data/{db,internal_media,logs} # 启动容器 docker run -d \ --name librephotos \ -p 3000:8001 \ -v ./librephotos-data/db:/db \ -v ./librephotos-data/internal_media:/protected_media \ -v ./librephotos-data/logs:/logs \ -v /path/to/your/photos:/data \ -e SERVE_FRONTENDtrue \ -e DB_BACKENDsqlite \ reallibrephotos/librephotos-unified:latest把/path/to/your/photos替换为你的真实照片目录。目录结构含义./librephotos-data/db/SQLite 数据库librephotos.sqlite3、cache.sqlite3./librephotos-data/protected_media/处理后的图片、缩略图与模型数据./librephotos-data/logs/应用日志与 Secret Key。1.4 SQLite 的生产化调优日志中提到为了让 SQLite 正常工作做了多处修复。在 production_noproxy.py 中可以看到具体的生产级 SQLite 配置DATABASES { default: { ENGINE: django.db.backends.sqlite3, NAME: os.path.join(db_dir, librephotos.sqlite3), OPTIONS: { transaction_mode: IMMEDIATE, timeout: 5, init_command: PRAGMA journal_modeWAL; PRAGMA synchronousNORMAL; PRAGMA mmap_size134217728; PRAGMA journal_size_limit27103364; PRAGMA cache_size2000; , }, }, }要点包括WAL 日志模式、synchronousNORMAL、128 MB mmap、以及transaction_modeIMMEDIATE避免写冲突时的忙等待。同时当数据库不是 PostgreSQL 时django.contrib.postgres会从INSTALLED_APPS中剔除因为该应用注册的是仅 PostgreSQL 的查询与操作如SearchVector等全文检索表达式api/models/photo_search.py及 OCR 全文索引迁移会用到。若DB_BACKEND既不是sqlite也不是postgresql会抛出ImproperlyConfigured异常并提示合法取值。1.5 入口脚本做了什么entrypoint.sh 按顺序完成以下工作设置MPLCONFIGDIR到/protected_media/matplotlib避免 matplotlib 每次启动都重建字体缓存导出BASE_LOGS默认/logs与LOG_LEVEL默认INFO并强制创建日志目录写 secret.key 需要若SERVE_FRONTEND为true/1/yes/on不区分大小写则导出DJANGO_SETTINGS_MODULElibrephotos.settings.production_noproxy并执行python manage.py collectstatic --noinput依据DB_BACKEND执行python manage.py migrate若设置了ADMIN_USERNAME与ADMIN_PASSWORD则自动创建或更新管理员账号api.models.User可用ADMIN_EMAIL指定邮箱依次启动各类服务start_service all、start_cleaning_service、start_job_cleanup_service、clear_cache、build_similarity_index后台运行qclusterDEBUG1时用runserver 0.0.0.0:8001否则用 gunicorn默认 4 个 workerWEB_CONCURRENCY可覆盖timeout 3600--max-requests 2000。1.6 环境变量速查统一镜像可用的核心环境变量来自 unified README 与 compose 文件变量说明默认值SERVE_FRONTEND是否由 Django 直接服务前端false标准代理模式DB_BACKENDsqlite或postgresqlsqliteCSRF_TRUSTED_ORIGINS逗号分隔的受信任源生产必设空SECRET_KEYDjango 密钥自动生成DB_NAME/DB_USER/DB_PASS/DB_HOST/DB_PORTPostgreSQL 连接信息仅 postgresql 模式使用ADMIN_USERNAME/ADMIN_PASSWORD/ADMIN_EMAIL自动创建管理员不创建WEB_CONCURRENCYgunicorn worker 数4BASE_LOGS/LOG_LEVEL日志目录与级别/logs/INFOMAPBOX_API_KEY地图服务密钥空SKIP_PATTERNS扫描忽略模式空ALLOW_UPLOAD是否允许上传falseNEXTCLOUD_ENABLED是否启用 Nextcloud 扫描falseFEATURE_VIDEO/FEATURE_FACE_DETECTION/FEATURE_FACE_CLUSTER/FEATURE_IMAGE_CAPTIONING/FEATURE_REVERSE_GEOCODING/FEATURE_SCENE_CLASSIFICATION功能开关true从标准拆分部署迁移到统一部署的步骤备份数据库与照片 → 切换到docker-compose.no-proxy.yml→ 设置SERVE_FRONTENDtrue→ 按你的域名更新CSRF_TRUSTED_ORIGINS→ 重新up -d。官方说明该方案与标准代理部署完全向后兼容两种方式可任选。二、相册公开链接分享Public Sharing via link日志的第二项大特性是通过链接公开分享相册即不要求对方登录即可查看指定用户相册。它由模型AlbumUserShare、序列化器AlbumUserPublicSerializer与三个 API 视图共同实现。2.1 数据模型AlbumUserShareapi/models/album_user_share.py 定义了分享实体album与AlbumUser一对一关联enabled是否启用分享带索引slug公开访问标识唯一、最长 64 字符未设置时由ensure_slug()自动生成uuid4().hex[:12]冲突时追加-N后缀expires_at过期时间可空空表示永不过期五个分享选项字段share_location、share_camera_info、share_timestamps、share_captions、share_facesNone表示继承用户默认值True/False表示相册级覆盖。is_active()方法判定分享是否有效未启用返回 False有expires_at且已过期返回 False否则为 True。get_effective_sharing_settings()按相册覆盖 用户默认值 系统默认值全 False的优先级解析最终生效的分享选项系统默认值来自api.models.user.get_default_public_sharing_settings用户默认值来自owner.public_sharing_defaults相关字段与迁移见 api/models/user.py 与 0119_add_public_sharing_options.py。2.2 API 视图与路由实现位于 api/views/public_albums.py路由注册于 librephotos/urls.py方法路径视图说明POST/api/useralbum/makepublicSetUserAlbumPublic开启/关闭分享设置 slug、过期时间与分享选项校验相册归属非拥有者返回 403GET/api/public/albums/s/slug/PublicAlbumBySlug无需认证AllowAny按 slug 返回公开相册仅当enabledTrue且未过期时命中GET/api/public/albums/s/slug/photos/photo_id/PublicPhotoDetailBySlug无需认证获取相册内单张照片详情photo_id同时支持 UUID36 字符含 4 个连字符与 32 位十六进制image_hash两种格式PublicPhotoDetailBySlug还会过滤掉hiddenTrue或in_trashcanTrue的照片并把解析出的sharing_settings传入PublicPhotoDetailSerializer的 context从而在序列化层面依据分享选项决定是否输出位置、相机信息、时间戳、标题与人物标签等字段。公开相册通过/media/.../public-album/...路径服务缩略图见 api/tests/media_serving/test_unified_media_access_view.py。2.3 分享选项的实际效果公开分享不只是给个链接而是可细粒度控制对外暴露哪些元数据。SHARING_OPTION_FIELDS常量定义了五个开关分别控制share_location地理位置EXIF GPS 与逆地理编码地名share_camera_info相机型号、镜头、焦距等设备信息share_timestamps拍摄时间等时间戳share_captions照片标题/描述share_faces人物识别结果与人物标签。默认全部为 False即链接访客只能看到最基础的照片内容不会泄露隐私元数据相册所有者可在开启分享时逐项覆盖或统一配置用户级默认值。三、文件夹导航视图分页与子文件夹照片计数第三个新特性是文件夹导航视图 分页 子文件夹聚合照片计数配套前端新增了详情页面包屑导航与子文件夹无限滚动。3.1 后端实现核心是 api/views/album_folder.py 中的FolderNavigationViewSet路由注册为/api/folders/subfolders/librephotos/urls.py。该接口接收两个查询参数path要列出子文件夹的目录路径缺省时管理员取settings.DATA_ROOT普通用户取自己的scan_directorypage页码默认 1。实现要点性能优化PAGE_SIZE 100每页最多返回 100 个子文件夹且只对当前页的子文件夹批量计算照片数一次Photo.objects.owned_by(user).aggregate(...)聚合查询通过files__path__startswith前缀匹配统计而不是扫描整个目录树排序与过滤子文件夹按名称忽略大小写排序跳过以.开头的隐藏目录photo_count为 0 的文件夹不返回响应结构包含current_path、parent_path、subfolders列表以及分页元数据page、page_size、total_folders、total_pages、has_next、has_previous安全校验管理员仅能访问DATA_ROOT之内的路径普通用户仅能访问自己scan_directory之内的路径startswith前缀校验从根源上防止目录穿越路径不存在或不是目录时分别返回 400。配套测试 api/tests/albums/test_folder_navigation_subfolders.py 验证了排序alpha、Beta、Gamma按忽略大小写排序、空照片计数文件夹不显示、photo_count聚合正确、100 条分页边界、越界页码返回空列表、以及权限/路径校验等行为。3.2 前端联动日志中提到前端配套改动包括详情视图的面包屑路径Breadcrumb Path与子文件夹的无限滚动infinite scroll。这意味着在前端可以沿着current_path/parent_path逐级向上回溯形成面包屑并利用has_next分页元数据在滚动到底时自动请求下一页形成流畅的目录浏览体验。相关实现可参考 apps/frontend/src/api_client/folders/ 下的 API 客户端封装。四、其他性能与稳定性修复日志还列出多项性能与稳定性改进可在源码中找到对应证据减少多处视图的查询次数perf pass例如删除操作修复 n1 查询、AlbumUserShare查询使用select_related(share, owner)预取关联对象迁移稳定性修复缺失图片路径时的迁移稳定性相关迁移见 0114_add_file_path_unique.py 与 0115_cleanup_duplicate_photos.py前端修复查看器中的 GIF 动图、无时间戳照片的报错、删除确认对话框改进、登录标题居中其他Docker 使用原生 runner 而非模拟器构建AMD64/ARM64 多架构支持依赖与社区翻译字符串更新。五、总结与适用建议想极简起步用librephotos-unified镜像 DB_BACKENDsqlite一条docker run即可跑通deploy/docker/unified/README.md生产环境推荐 compose 方案deploy/docker/unified/docker-compose.no-proxy.yml用 PostgreSQL、设置SECRET_KEY与CSRF_TRUSTED_ORIGINS并置于 Traefik/Caddy 等反向代理或云负载均衡之后该方案对自动 HTTPS 友好分享相册通过公开链接功能生成 slug 链接并用五个分享选项控制对外暴露的元数据粒度浏览大目录使用文件夹导航视图逐层浏览利用分页与子文件夹照片计数避免一次性加载海量目录。这些功能均已在 2025-08-28 的开发日志 中宣布本文结合仓库源码补充了底层实现细节供你在部署、排障与二次开发时参考。【免费下载链接】librephotosA self-hosted open source photo management service.项目地址: https://gitcode.com/GitHub_Trending/li/librephotos创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表