序※
自建图床总是崩溃?探索一种全新的图床方式。
我用过兰空图床,太庞大且架构比较古老。因为更新之后爆炸了,数据库表结构一直有问题被迫放弃。
从这之后我愈发觉得,把我的所有数据抽离出来,单独维护是很重要的。
然后我去用了picsur,比较轻量,也比较现代,基本功能都有,但数据方面还是不满意,而且没有中文。
我现在觉得更好方式是把存储和元数据解耦,把图片放在云上,然后把图片的元信息包括标题、文件名、外链等信息单独放在数据库里
这样数据和图片可以分离,不会一起爆炸丢失数据。
架构※
主要是三个部分,图片、数据和关联的方法
图片※
图片我想放在 R2 比较理想,毕竟CF是赛博活菩萨,有现成的API,上传后可以直接拿到外链,甚至可以直接通过cf优化访问速度
R2免费空间有10G,看着不大,但我把图片统一转为webp
格式上传,一般webp
图片的大小在50~100kb, 也就是说10G可以放十万到二十万个图片,我觉得给我的Trilium
笔记/博客用是足够了。不够用后续也只要$0.015 / GB-month
,就是大概 ¥0.1/GB/mo
,其实并不贵(对比良心云和套路云
数据库※
我觉得把图片的元数据剥离对数据安全性有很大的提升。数据库可以放服务器,也可以用云平台的Paas
, 比图床这种单体应用更加健硕。
方法※
就是包括上传图片、维护数据库、查询外链等方法,我用Django
来做了一个Web应用,我管祂叫POTO ,已经在github开源。
其实可以直接用Picgo, 但我更喜欢用BS架构,因为网页在哪都能用。另外Picgo也要安装插件才能用S3
用Django其实是因为它和数据库交换方便,而且可以轻易的更换数据库。(我也是最近才了解Django这个框架,正好练练手
把图片和数据解耦也可以更方便的在两个视图(文件和数据)进行操作。
过程※
存储※
查阅CourFlar R2文档,R2可以通过两种方式使用
- Workers : 可以直接操作文件,上传、下载、修改、设置访问全新等等
- S3 API :部分兼容AWS的API, 因为这个API通用性很强,多数云存储工具都支持。
我选择使用S3 API,
- 一是通用性更强,(写完接口才发现是S3, 这样这个
poto
工具也可以在和很多平台使用,比如Alist, 可以关联到百度网盘、OneDrive等几十种云介质) - 二是写Workers比较麻烦,使用Workers还可能遇到网络问题
数据库※
通用的关系型数据库我选择了MariaDB,因为这是Arch linux默认用的,尽量把这些数据放在服务器的系统数据库里集中管理,也好备份。
表结构:
id
主键 维护数据和图片关系的键,选这个键是作为文件名,因为上传到R2不会重名,R2 API重名会覆盖文件,这是一个棘手的问题,重名需要很多逻辑去处理,包括查询有无同名文件,上传的错误捕获等。文件不重名就省事了。也考虑过用hash或时间戳,但是这些也有几率重名的,比较麻烦title
后面想做查找图片的功能,title可以标记文件主要信息,方便以后复用。upload_at
上传时间url
: 外链,其实可以用外链的域和id拼接,但考虑到以后可能会拓展到不同的云存储,外链可能不同,就新建了一个字段
在数据库方面,用Django的好处就体现出来了,可以灵活的切换数据库,通过model绑定键值信息,我原来不是很喜欢Python, 尤其是写Web应用,但。。真香
方法※
- 上传页面,处理元信息,转换为Webp上传R2, 最后写入数据库。
- 获取外链,直接查询数据库,拿到外链,在页面渲染图片,展示外链。
- Todo:批量上传
- Todo:格式转换
- Todo:查询图片,通过搜索title找到图床有的图片。
- Todo:多用户,复用Django的用户管理,把图床开放给其他用户。
- Todo:维护工具,获取文件列表比对数据库,修改图片和数据等
poto怎么用?※
克隆我的仓库
$ git clone https://github.com/tsaitang404/poto $ cd poto
配置虚拟环境
$ python -m venv django $ source django/bin/activate
安装依赖
$ pip install -r requirements.txt
构造表结构
$ python manage.py makemigrations $ python manage.py migrate
创建管理员用户
$ python manage.py createsuperuser ## 按照指引新建管理员用户
运行poto
$ python manage.py runserver 0.0.0.0:8000
一些其他的生产部署的设置,参见Django发布到生产, 还有我的笔记版
设置反向代理
## nginx配置模板 server { listen 80; server_name your.domain; return 301 https://$server_name$request_uri; # 重定向到https } server { listen 443 ssl; server_name your.domain; ssl_certificate /path/to/cert.pem; # 指定 SSL 证书 ssl_certificate_key /path/to/key.pem; # 指定 SSL 密钥 client_max_body_size 200M; # 指定body最大长度, 决定可上传图片的最大大小 location / { proxy_pass http://127.0.0.1:8000; # 将请求转发到 Django 服务器 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location /static/ { alias /path/to/staticfiles/; # 静态文件位置,注意文件权限是否能被nginx用户读取,否则会403错误 } }
作为服务运行
## 修改potp.service的WorkingDirectory和ExecStart的路径 # ln poto.service /etc/systemd/system/poto.service # systemctl restart poto nginx
结果咋样?※
- 部署完成后进入
your.domain/admin
- 用管理员登录
- 进入Configuration设置
- 右上角 Add configuration
- 填写以下字段:
endpoint
:R2的终结点,形如https://<account-id>.r2.cloudflarestorage.com
,也可以是其他s3终结点access_id
:S3 API的访问IDsecret_access_key
:S3 API的Token 可以在R2页面右侧→ 管理 R2 API 令牌页面生成bucket
: 储存桶名字access_url
: 外链地址 可以进入储存桶在设置中设置连接你的子域或使用r2.dev
子域,设置允许公开访问,或者设置CORS 策略限制访问。password
:访问密码
进入上传页面your.domain/upload
,登录,选择、粘贴、拖拽图片,填写title,上传。
直接获取外链 eg. 我的头像 点击链接复制外链
然后去要用的地方粘贴即可!
跋※
后来我发现Alist也更新了S3 接口,可以用poto工具上传Alist的各种介质,真不错。
但我还是选择了R2,因为作为外链太方便了!