自建云相册PhotoPrism
前言
记得我是2016年开始用Office365,家庭版一年229,6个人拼车,人均40。除了可以畅享正版Office外,还有1T的OneDrive空间。从那时候开始,就把存放在电脑里10多年的老照片都放到了OneDrive里。此外使用OneDrive的APP,还可以把手机里面的照片同步到OneDrive上。最重要的是,OneDrive提供了基于AI的照片Tag,可以按地理位置或者Tag进行照片分类,极大方便了照片备份和管理。
然而,2020年不知道什么时间开始,OneDrive的Tag功能消失了。经过多方面了解,得知微软官方确实下线了Tag的功能,但理由众说纷纭。有说功能会被转移到Bing上,但目前没有任何进展;有说因为数据隐私的原因;还有说牵扯三星的相关专利问题。具体可以在Microsoft Community上查看 “Edit Tags” option missing in One Drive & existing tags not visible. 同时希望Tag功能回归的请愿已经放到了UserVoice上,里面有来自用户的各种吐槽,但官方没有任何表示。而在OneDrive的support页面,关于Tag的功能描述依然存在。
不管什么原因,未来计划如何,这个功能现在确确实实没有了。对我来说,200多G的照片的分类管理一下没了办法,于是开始寻找替代方案。
Google Photo作为最好用的云相册是最优先被考虑的,然而网络是个大问题,即便有梯子,还是显得麻烦一些。最重要的是,谷歌居然调整了相册的收费策略,从以前的高质量图片无限存储变成了15G限制,2021年6月生效,超过需要付费购买空间,价格比OneDrive贵不少。谷歌、微软一番操作,不得不让人疑惑,不让人警惕。至于国内,百度一刻、阿里云盘、时光相册等,未来跑不跑路、用户当不当韭菜不说,动不动强行净化一波网盘资料,就更不敢上车了。
SaaS产品没谱,就考虑自建。想过NAS,也考虑过路由器+移动硬盘的NAS替代方法,但终究不想给家里增加这么多电子垃圾。
几经波折,在Github上看到了PhotoPrism这个项目,使用Golang开发,TensorFlow进行照片分类,支持WebDAV协议,并且已经有10多k的star了。因为我在阿里和腾讯云都有轻量服务器,用来部署这个方案最适合不过了。于是话不多说就开始折腾,先后调整了3个方案,汇总如下:
方案1:轻量服务器+对象存储+PhotoPrism mobile App
1. 挂载对象存储
不论阿里还是腾讯,轻量服务器的磁盘空间都是比较少的,我购买的2核U4G内存3T流量的实例,磁盘只有80G,远远不够相册存储。只有给服务器挂载块存储,文件存储或者对象存储才能解决存储问题。其中块存储和文件存储的价格都比较高,所以最后选择了服务器挂载对象存储的方案。
具体的挂载方式可以参考阿里云的OSSFS和腾讯云的COSFS工具,这里就不多说了。最终实现的效果是,把对象存储的Bucket挂载到“ /mnt/photoprism ”目录。
需要注意的是对象存储最好和轻量服务器同一个区域,这样就可以使用内网流量进行通讯了,避免产生对象存储的外网下行流量。
2. Photoprism的搭建
我选择用Docker-Compose的方案,官方有具体的教程 https://docs.photoprism.org/getting-started/docker-compose/ 这里简单陈述下步骤:
-
下载配置文件
1 2 3 4 5
# 路径仅供参考 mkdir /home/ubuntu/photoprism cd /home/ubuntu/photoprism wget https://dl.photoprism.org/docker/docker-compose.yml vi /home/ubuntu/photoprism/docker-compose.yml
-
修改配置内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
version: '3.5' services: photoprism: # 使用预览版本: image: photoprism/photoprism:preview # 开机自启动 restart: unless-stopped security_opt: - seccomp:unconfined - apparmor:unconfined ports: - 2342:2342 environment: #设置登录密码 PHOTOPRISM_ADMIN_PASSWORD: "xxxxxx" # PLEASE CHANGE: Your initial admin password (min 4 characters) PHOTOPRISM_HTTP_PORT: 2342 # Built-in Web server port PHOTOPRISM_HTTP_COMPRESSION: "gzip" # Improves transfer speed and bandwidth utilization (none or gzip) PHOTOPRISM_DEBUG: "false" # Run in debug mode (shows additional log messages) PHOTOPRISM_PUBLIC: "false" # No authentication required (disables password protection) PHOTOPRISM_READONLY: "false" # Don't modify originals directory (reduced functionality) PHOTOPRISM_EXPERIMENTAL: "false" # Enables experimental features PHOTOPRISM_DISABLE_WEBDAV: "false" # Disables built-in WebDAV server PHOTOPRISM_DISABLE_SETTINGS: "false" # Disables Settings in Web UI PHOTOPRISM_DISABLE_TENSORFLOW: "false" # Disables using TensorFlow for image classification PHOTOPRISM_DARKTABLE_PRESETS: "false" # Enables Darktable presets and disables concurrent RAW conversion PHOTOPRISM_DETECT_NSFW: "false" # Flag photos as private that MAY be offensive (requires TensorFlow) PHOTOPRISM_UPLOAD_NSFW: "true" # Allow uploads that MAY be offensive # PHOTOPRISM_DATABASE_DRIVER: "sqlite" # SQLite is an embedded database that doesn't require a server PHOTOPRISM_DATABASE_DRIVER: "mysql" # Use MariaDB (or MySQL) instead of SQLite for improved performance PHOTOPRISM_DATABASE_SERVER: "mariadb:3306" # MariaDB database server (hostname:port) PHOTOPRISM_DATABASE_NAME: "photoprism" # MariaDB database schema name PHOTOPRISM_DATABASE_USER: "photoprism" # MariaDB database user name PHOTOPRISM_DATABASE_PASSWORD: "insecure" # MariaDB database user password PHOTOPRISM_SITE_URL: "http://localhost:2342/" # Public PhotoPrism URL PHOTOPRISM_SITE_TITLE: "PhotoPrism" PHOTOPRISM_SITE_CAPTION: "Browse Your Life" PHOTOPRISM_SITE_DESCRIPTION: "" PHOTOPRISM_SITE_AUTHOR: "" volumes: # 映射原始文件存储目录为挂载的对象存储的Bucket - "/mnt/photoprism/pictures:/photoprism/originals" # 缓存、索引文件,可映射到对象存储Bucket - "/mnt/photoprism/storage:/photoprism/storage" mariadb: image: mariadb:10.5 restart: unless-stopped security_opt: - seccomp:unconfined - apparmor:unconfined command: mysqld --transaction-isolation=READ-COMMITTED --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --max-connections=512 --innodb-rollback-on-timeout=OFF --innodb-lock-wait-timeout=50 volumes: # 数据库文件存储在宿主机/home/ubuntu/photoprism/database下 - "./database:/var/lib/mysql" environment: MYSQL_ROOT_PASSWORD: please_change MYSQL_DATABASE: photoprism MYSQL_USER: photoprism MYSQL_PASSWORD: insecure # 开启自动更新 watchtower: image: containrrr/watchtower restart: unless-stopped volumes: - "/var/run/docker.sock:/var/run/docker.sock"
-
运行docker-compose
1
docker-compose -f /home/ubuntu/photoprism/docker-compose.yml up -d
3. 反向代理
部署完成后,相关的反向代理可以参考官方文档 https://docs.photoprism.org/getting-started/proxies/nginx/ ,我这里做了一些修改,主要是使用了SSL证书做了https重定向。
|
|
4. Web访问
使用反向代理的域名登录PhotoPrism后,就可以创建相册, 然后上传照片了。第一次使用,上传完成后,可以手动做一次索引,以后使用每15分钟会自动索引。关于索引更多的内容可以看官方文档 https://docs.photoprism.org/user-guide/library/indexing/
PhotoPrism支持分辨率的自适应,也就是说不论在PC、平板还是手机,都可以访问使用。同时可以看到PhotoPrism丰富的菜单栏和照片分类方式,包括基于时间线的日历、基于AI的Tag分类、基于地理位置的分类。虽然目前还没有基于人脸识别的分类,但在RoadMap上已经安排上了,相信很快就会发布。
5. APP同步手机照片
使用Web访问,可以导入历史照片数据,但手机照片的备份还需要APP配合使用。PhotoPrism官方提供了APP,使用Flutter开发,但目前还处于非常初期的阶段,功能不完善,并且与当前版本的后台还不兼容。虽然已经有Contributor提供了的方案和PR,但还没有被合并。安装使用后,发现只有最基本的照片查看和相册管理,没有分类视图,自动上传手机照片也是实验性功能,不能在后台工作。看来一段时间内,都很难用官方的APP来满足实际需求了。不过可以预见的是,等Google Photo开始收费后,这个项目会被越来越多的人关注,如果有擅长Flutter的小伙伴,不妨加入Contributors的队伍啊,毕竟现在才有9人。

方案2:轻量服务器+对象存储+PhotoSync App
因为方案1没有很好的解决手机照片同步的问题,所以又在寻找官方APP的替代品。
这里看到官方文档推荐了使用Photo Sync来作为同步APP,这是一款比较成熟的跨平台照片同步产品,支持FTP、WebDAV等多种协议的数据传输。因为PhotoPrism支持WebDAV协议,所以只需要对PhotoSync进行WebDAV服务器的设置,就可以实现手机相片同步了。
不过PhotoSync要使用自动备份功能,如充电时同步、拍照后同步、连接到指定WIFI后同步等,需要付费购买插件,大概20大洋左右,而且安卓APP没有中文。不过使用这样的方案组合,在官方APP没有完善之前,确实满足了需求。只是在APP上没办法像使用WEB那样查看照片分类,只能用手机访问WEB进行查看,算是一个遗憾了。
方案3:轻量服务器+OneDrive
方案1、2已经可以满足需求了,但折腾一圈之后发现成本有点高。
- OneDrive:229/年,6人分摊后40/年
- 轻量服务器:800/年,5人分摊后160/年
- 对象存储500G:600/年
- 对象存储请求次数:按量付费
- 对象存储外网下行流量:与轻量服务器同区域可免外网流量
整套下来一年800多一年,比SaaS产品和NAS都贵多了。然后对象存储请求次数,官方定义为使用API或SDK上传、下载、删除等操作对象存储均会产生请求次数。因为对象存储是挂载到服务器的,所有的操作本质上还是调用了对象存储的接口,几百G的照片上传,索引,分分钟百万千万的请求次数,虽然请求次数很便宜,几十万次数才几块钱,但羊毛薅多了也顶不住啊。
所以回归到成本角度,还是败给了Money,于是考虑降本方案。
我是在方案2搭建完成后,开始考虑怎么把OneDrive的数据同步过来。如果先下载到本地电脑,再上传云服务器,除了OneDrive那个下载速度本来就很玄学不说,这样一来一回400多G,实在是太傻了。所以就找到了rclone这个项目,它支持灰常灰常多的数据存储产品之间同步。其中少不了OneDrive,根据官方文档介绍https://rclone.org/onedrive/ 很容易就实现了数据从OneDrive直接同步到云服务器。因为服务器在香港,所以速度也比下载到本地电脑快不少。
正是在使用rclone的时候,无意中看到它还支持把上面这些存储产品作为磁盘挂载到主机,那也就是说我可以直接挂载OneDrive作为存储,这样以来对象存储什么的都不要了,连旧数据的同步都免了,真香啊!操作起来:
1. 安装rclone
在轻量服务器上安装rclone,参考官方文档 https://rclone.org/install/ ,linux一键脚本
|
|
2. 配置OneDrive
- 在轻量服务器上配置OneDrive,参考官方文档 https://rclone.org/onedrive/ 或按以下步骤
|
|
因为轻量服务器没有GUI,所以没法打开浏览器进行访问,所以在配置的最后一步,选择了n,进入了上面的result等待界面,这时候需要远程完成rclone的配置。详细信息可参考官方文档 https://rclone.org/remote_setup/,我这里简单陈述步骤。
- 在任何可以打开浏览器的电脑,最好是Linux系统进行操作,可以用虚拟机,这里我选择了本地电脑的WSL
|
|
- 粘贴复制内容到轻量服务器result,完成rclone配置,此时在轻量服务器执行命令
|
|
|
|
3. 方案设计
在挂载OneDrive之前,需要先了解PhotoPrism和OneDrive的一些特性,才能设计挂载的方案:
-
PhotoPrism上传文件后自动索引,仅在WebDAV协议下生效。也就是说它是通过监听协议来触发自动索引的,不论是PhotoPrism的Web,还是它官方的APP,还是前文中介绍的PhotoSync,都是基于WebDAV协议来实现文件上传的。手动拷贝文件到PhotoPrism的Originals文件夹,并不会触发自动索引。而我们使用的是OneDrive作为磁盘挂载的方案,在PhotoPrism的Web上传图片没有什么影响,是会自动索引的。但使用OneDrive的APP同步手机照片上来,就没办法走WebDAV的协议来触发索引了。当然你可以使用PhotoSync来作为手机照片同步工具,但既然用了OneDrive的方案,能不能用OneDrive的APP来实现手机照片同步,并且完成索引呢。关于更多自动索引的讨论可以看官方issues https://github.com/photoprism/photoprism/issues/281
-
PhotoPrism手动创建索引有两种方式:一种是前文中介绍过的通过Web来创建索引,另一种是通过命令行执行命令来创建索引。而命令行创建索引又有两个不同的模式选项:一个是重新索引文件,包括未更改的文件。另一个是清除所有的索引和缩略图后重新索引。照片文件被索引的过程包括通过TensorFlow进行分类,解析地理位置,解析时间等,随后生成索引文件,最后还要生成缩略图存储在缓存文件中。正是因为步骤繁琐,所以第一次索引时间会很久,而后续索引因为已经存在缩略图等缓存文件,所以就快一些。那么也就意味着命令行创建索引,使用cleanup的参数会更彻底一些,但速度也会更慢一些。
1 2 3 4 5 6 7 8 9 10
photoprism index --help NAME: photoprism index - Indexes media files in originals folder USAGE: photoprism index [command options] [arguments...] OPTIONS: --all, -a re-index all originals, including unchanged files --cleanup removes orphan index entries and thumbnails
-
PhotoPrism的Import功能是把指定的Import目录下的文件导入到Originals文件夹,并进行索引,然后删除Import目录下的文件。一般用于导入大量的历史数据。也可以用于定时导入新增数据,因为Import文件夹一般数据比较少,而且导入完就可以删除,不需要索引整个Originals文件夹,索引时间不会很久。导入的方式也有两种,一个是控制台,另一个是命令行。
1 2 3 4 5 6
photoprism import --help NAME: photoprism import - Moves files to originals folder, converts and indexes them as needed USAGE: photoprism import [arguments...]
-
OneDrive同步手机照片到云端的路径是默认的,如我的路径是默认在“OneDrive/图片/本机照片”下,无法手动选择。但可以移动这个默认目录并且重命名,如我移动了该目录并改名为“OneDrive/Camera”,实测以前上传的照片没有丢失,而新同步的照片就到了这个目录下。需要注意的是微软官方并不建议移动和修改这个文件夹的名称。所以提醒各位风险需要自行评估。
基于以上特性,最终设计的方案就是:
- 使用OneDrive APP进行手机照片的同步(如果使用PhotoSync作为同步APP,则可以跳过第7步,无需做定时任务。但考虑到尽可能少引入工具,这里使用了OneDrive APP来同步手机照片)
- 使用PC或手机访问PhotoPrism的Web查看照片
- 在OneDrive下新建一个文件夹
photoprism
,挂载到服务器/mnt/onedrive/originals
,并映射docker的/photoprism/originals
目录,用于photoprism的原始照片存储 - 将OneDrive同步手机照片的默认文件夹,挂载到服务器
/mnt/onedrive/import
,并映射docker的`/photoprism/import目录,用于photoprism定期导入 - 在服务器执行import命令,将OneDrive的历史照片导入并完成索引
- 在服务器新建定时任务,定期执行import命令,将OneDrive新同步的照片导入并完成索引
4. 挂载OneDrive
- 创建目录
|
|
- 创建服务
|
|
- 启动服务
|
|
- 取消挂载
|
|
5. 修改PhotoPrism配置文件
- 见方案1,修改了dockerfile volume部分
|
|
- 重启容器
|
|
6. 索引历史数据
PhotoPrism搭建完成后,需要把历史数据全部导入。跟前文提到的一样,有两种方式
- 手动操作:登录Web,进入“库”,进入“导入”页面,勾选“移动文件”,点击“导入”
- 命令行操作
|
|
我有200G的历史数据图片,每个图片索引差不多需要5-8秒,服务器CPU飚的飞起。以为是因为挂载OneDrive网络性能不足,导致索引很慢。不得不说,性能多少有影响,但索引步骤复杂,终究需要一些时间。所以让子弹飞一会吧,最终花了差不多3天时间才全部导入并索引完成。
7. 创建定时任务
前文也提到过,手动拷贝文件PhotoPrism不会自动索引,那我们通过OneDrive APP同步的手机照片就需要定期做同步并完成索引。这里就可以创建一个定时任务
|
|
方案3使用了OneDrive作为存储,手机照片同步依然用回OneDrive,与之前的备份习惯无缝衔接。当然,OneDrive APP不能访问PhotoPrism,如果需要看照片,还是需要通过手机浏览器访问Web,短期内没有什么影响。等官方的APP完善后,就可以使用官方APP来进行照片同步,也就不需要定时导入和索引了。
至此,基于轻量云服务器的云相册搭建完成,撒花。