首先还是要提一下,又是一篇咕了很久的文章了,去 git 看了下自己的提交记录,6 月中的时候就已经弄好了,最近一次提交是在 6 月 22 号的时候,加上了博客随机图服务,oaPlayerNoaPlayer 的模块,但是一直咕着没有写文章,趁这周末有时间有心情的时候回顾一下

草稿里面还有几篇,最近咕的好厉害(比W略强),后面慢慢补吧

前言

这回考察新图床的原因自然是在用的 V2 版图床 Cloudreve 做图床有一些不尽人意的地方,大概是这些

  • 没有明晰的 API 文档/接口
  • 设计主要是作为私有云盘使用的,图床算是边际功能
  • 上传图片不能自动压缩
  • 使用 sqlite (可选 mysql/pgsql ,但是我本地没用,用 mysql/pgsql 的时候应该速度会快一些,但是我不大想动它了),外链多的时候(超过 2w+ 个)速度显著下降
  • 没有和文档写作软件(我用的 Typora )的无缝集成。上传图片需要手动操作复制链接,很麻烦
  • 外链的链接非常长,有些地方会不兼容(说的就是你 Plumemo ,V2 博客的时候里面的 Logo 都是放在可道云的,这就是为什么我一直想把我的可道云关站却不能关的原因)

需求

在找新图床之前,先康康自己的需求/要求

  • 【必须】语言要求:不能是 PHP ,洞太多了,之前看可道云天天安全更新麻了,可以说一大帮搞安全的就指着 PHP 过活了,最好是 Go 写的,轻量(自己服务器余量很少了)
  • 【必须】数据存储在本地,白嫖/云服务不可靠,我个人的话,不愿意把数据放在云端
  • 【必须】备份/迁移方便
  • 【最好】原生支持 K8S ,未来自己的 Docker 都要打算往 K8S 里面迁移
  • 【必须】多节点支持,方便扩容
  • 【必须】明晰的 API 接口/文档,方便做二次开发
  • 【必须】图片自动压缩,节省空间
  • 【必须】和文档写作软件( Typora )的无缝集成
  • 【必须】除了能传图片视频之外,要能传其它文件(附件)
  • 【最好】链接要不好猜,要有一定程度的鉴权,不能把整个图床的资源全部一个列表开放出去
  • 个人使用,不需要考虑多租户等问题,管理方面可以简单一些,有无后台 WEB 均可

选型

看了一下 GitHub 的图床项目,没有看中的,需求和要求雀食有点儿太多,必须来点儿二次开发哩

以前研究过一点点 CEPH ,太重了,那会儿也关注了 MinIO,现在很多 OSS ( S3对象存储,亚马逊开创的)都是它,像阿里,京东都在用它(当然是改着用),很轻量,云原生,很自然的就联想到了它,我们来康康上面的需求它能实现哪些

  • 语言:Go,洞不是很多
  • 数据存储在本地
  • 备份和迁移很方便,rsync 就能搞定
  • 云原生,K8S 没问题
  • 原生多节点支持,官方推荐跑三节点,当然单节点也能跑,只是数据安全没法保证了(当然我们在虚拟机跑,宿主机底层有 RAID ,我觉得这个时候跑单节点就行,不会丢数据,等服务器未来升级了有余量的时候往多节点迁)
  • 文档非常详细,S3 是开放标准,不说二次开发,甚至有很多的东西可以直接拿来用
  • 作为对象存储,那不仅仅图片、视频,各种各样的文件都可以存

当然选它的话有些问题就需要自己来解决

  • 图片自动压缩,MinIO 不是图床,没有这方面的考虑和设计
  • 和文档写作软件的无缝集成,这个也需要自己开发
  • 公共存储桶的鉴权,因为只想从博客文章把链接给出去,不想直接把整个存储桶的文件列表给出去,不然实在太好爬了
  • 文件的简单管理,虽然 MinIO 有个 WEB 可以做简单的管理,但是不可能每次都到 WEB 去管,而且文件多了也很难在里面找到要改动的文件,像下面这样几百上千个文件的时候,根本没法去找不是
image-20230806142624787

架构设计

采用 MinIO + 自行开发综合业务网关 NoaHandler 实现,自行开发的综合业务网关 NoaHandler 架构见下图

打造个人图床+综合业务网关,cloudreve迁移到MinIO

在网上对于 OSS 的存储是否应该自己再做一个网关的争论很多,这里因为我需要实现一些 MinIO 本身不支持的功能,加上服务都在本地内网运行,带宽是非常足够的,所以选择了自己再做一个业务网关,不直接去调 MinIO 的服务

关于图片压缩,文件的可视化管理,和文档写作软件的集成……这些都放在了客户端去做,图片压缩是因为 webp 的转换比较占资源,服务器资源余量不多了。文件可视化管理纯粹是因为我不大会写前端,所以只能用客户端软件的方式提供。文档写作软件的集成,这个必须在客户端这边来做

语言的话基本上都是选择用 Go 来做,全平台,部署简单,速度快,我很喜欢

服务端介绍

服务只有一个自己写的软件:Noahandler ,使用 Go 来编写,目前没有开源,有三大模块:数据模型,业务处理器,子功能模块,几个之间的关系大致如下

  • 数据模型:很好理解,作为面向对象的语言,需要有模型,业务处理器和子功能模块处理到这些对象的时候会去把模型实例化

  • 业务处理器:对象存储网关是必选模块,剩下的仿哔咔服务端、OAPlayer/NoaPlayer 模块,博客随机图模块作为可选模块。业务处理器接收客户端发来的数据(绝大多数是 json )调用数据模型和子功能模块,处理并返回结果给客户端

  • 子功能模块:比较多,业务处理器只关心业务,也就是只关心客户端发来了什么数据,要返回客户端什么样数据,客户端鉴权,业务处理全部都是在子功能模块实现的。

    数据库模块负责数据库的初始化(我个人很讨厌建表语句,我认为建表这种事情应该是由程序自己去做的,即程序自己判断是否需要建表,哪些表需要去建,而不是扔一条建表语句或者是 sql 让运维去跑)和连接;

    文件头判断模块判断文件的实际类型(根据文件头,之前见过某些系统是用文件后缀名判断的,很好骗),根据白名单判断是否放行到 MinIO ;

    文件哈希计算模块解决公开存储桶的鉴权问题,NoaHandler 以存储桶粒度提供了两种文件存储方式,一种是文件名存储,有文件名过滤模块和 WAF 防止注入问题,一种是哈希存储,计算文件的哈希作为文件名,主要是在公开存储桶使用,防止外部访客按照文件名猜文件出来,某种程度上也算是一种鉴权;文件管理模块负责文件的增删改;

    对象存储操作模块负责与 MinIO 交互,文件存储模块负责文件的增删

    调用鉴权模块负责验证客户端是否获得了操作权限

基本上都是模块化设计,方便未来业务的增删改

数据库采用了 mysql + pgsql ,mysql 是主要数据库,pgsql 是兼容 OADrive 的数据库考虑

客户端介绍

客户端这边比较多,有一部分开源了,主要使用 Go 编写,目前客户端有以下五个,按照开发时间顺序排列:

  • OADrive
  • yuukaUploader
  • yuukaViewer
  • typoraUploader (开源)
  • OAPlayer (开源)

OADrive

没有开源,白嫖某平台当云盘的客户端,采用 pgsql 做云端数据存储和同步,本地存储是 SQLite ,开发语言是 Go ,编写于 22 年 8 月底

NoaHandler 对接了它的数据库用来向 OAPlayer 提供服务

image-20230806145936939

yuukaUploader

没有开源,NoaHandler 的采集&管理客户端,使用 SQLite 存储少量平台数据,开发语言是 Go ,编写于 23 年 6 月初

主要功能是 NoaHandler 的平台管理,MinIO 文件的可视化上传和删除,仿哔咔服务端的管理,博客随机图管理,NoaPlayer(OAPlayer) 播放列表的管理

image-20230806150239884

yuukaViewer

没有开源,仿哔咔客户端,有 Windows 和 Android 两种形态的客户端,其它平台未编译,采用 Go 编写,编写于 23 年 5 月

Windows 端

image-20230806151251283

安卓端

image-20230806150951356

typoraUploader

开源地址:https://github.com/luckykeeper/typoraUploader ,Typora 图片上传插件,命令行模式使用,需要搭配狗歌的 libwebp 使用,自动转图片到 webp 并上传 MinIO ,采用 Go 编写,编写于 23 年 6 月

image-20230806151622400

OAPlayer

开源地址:https://github.com/luckykeeper/OAPlayer ,一个简单的视频播放器,主要为了解决 Cloudreve 播放器不能循环播放/不能列表播放的问题,听歌用,采用 Flutter ( Dart ) 编写,编写于 23 年 6 月

Windows 端

OAPlayer_01 OAPlayer_02

Android 端(可随屏幕旋转)

image-20230806152246834

评价

我个人对这套 MinIO 的个人图床存储 + 综合业务网关的方案非常满意,解决了现有 Cloudreve 使用上的诸多不便和未来可能遇到的性能问题,可扩展性极强,属于是非常个人定制化的方案了,应该很少会有人有这样的需求,如果有类似的需求的话,不妨试试 MinIO ,搭建起来很容易,开发也很简单,尤其是用 Go 的时候,MinIO 是 Go 写的,同时它也提供了非常好用的 Go 客户端(库)