# 1. Chevereto简介
图床:也就是专门提供存储图片的地方,我们上传好图片,就可以通过外链/API等方式访问了。图床分为公共图床和自建图床。
- 公共图床,会用各种技术帮我们做图片相关的优化和服务,比如CDN 加速、图片处理、图片鉴黄、文本识别等等,我们不需要担心硬盘空间不足等问题。
- 而下文介绍的 Chevereto (opens new window) 属于私有图床,将其建立在国内服务器或者套上合适的CDN,都可以为我们访问照片加速,大大加快打开网页的速度。除了储存、处理、分发照片外,Chevereto还可以作为个人分享和收集优秀图片的平台。
# 2. 搭建前的环境准备
以下我将采用Nginx+Docker部署的方式进行搭建,VPS系统用的是Debian 11 x86_64。VPS的购买及配置、域名解析、Docker的概念及使用...这些基本的就不再赘述了,如果不会的话见我的其他博客:VPS基本部署环境的搭建与配置 (opens new window)、Docker容器化及项目环境管理 (opens new window)。
# 2.1 Docker环境搭建
$ apt-get update -y && apt-get install curl -y # 安装curl
$ curl https://get.docker.com | sh - # 安装docker
$ sudo systemctl start docker # 启动docker服务
$ docker version # 查看docker版本(客户端要与服务端一致)
2
3
4
# 2.2 Docker-MySQL环境搭建
# 2.2.1 拉取镜像并创建实例容器
$ docker pull mysql:5.7
$ docker run -p 3306:3306 --name mysql \
-e TZ=Asia/Shanghai \
-v /mydocker/mysql/conf:/etc/mysql/conf.d \
-v /mydocker/mysql/logs:/var/log/mysql \
-v /mydocker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=[password] \
-d mysql:5.7 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
2
3
4
5
6
7
8
命令解释说明:
-p 3306:3306:将主机的3306端口映射到docker容器的3306端口。
--name mysql:运行服务名字
-e TZ=Asia/Shanghai:时区是使用了世界标准时间(UTC)。因为在中国使用,所以需要把时区改成东八区的。
-e MYSQL_ROOT_PASSWORD=[password]:初始化 root 用户的密码。
-d mysql:5.7 : 后台程序运行mysql5.7
--character-set-server=utf8mb4 :设置字符集
--collation-server=utf8mb4_unicode_ci:设置校对集
2
3
4
5
6
7
设置开机自启
$ docker update mysql --restart=always
# 2.2.2 创建数据库及用户
在本地使用Navicat工具使用root用户连接上该数据库,使用如下四条命令创建数据库及用户。
--创建新的数据库,并设置数据库编码
$ CREATE DATABASE 你的数据库名 DEFAULT CHARSET=utf8 DEFAULT COLLATE utf8_unicode_ci;
--创建新的用户
$ CREATE USER '你的用户名'@'你的服务器IP' IDENTIFIED BY '你的密码';
--把数据库的管理权限给予刚刚创建的MySQL用户
$ GRANT ALL PRIVILEGES ON *.* TO '你的用户名'@'%' IDENTIFIED BY '你的密码' WITH GRANT OPTION;
--刷新权限,使用设置生效
$ FLUSH PRIVILEGES;
2
3
4
5
6
7
8
9
10
11
注:如果连接数据库时出现Access denied for user '用户名'@'某IP' (using password: YES)
问题,则是第三句授权出了问题,你的本地外网IP被拦截了,那个'%'代表的是访问IP不受限制。
# 3. 搭建Chevereto私有图床
Chevereto的搭建主要介绍Docker部署的方式,而通过官方提供的Installer部署的方式仅简要介绍注意事项。
# 3.1 拉取Chevereto的docker镜像
先使用docker search
命令搜索一下Chevereto的镜像,然后这里我们选用的是linuxserver/chevereto
镜像进行搭建。
$ docker search chevereto
$ docker pull linuxserver/chevereto
2
# 3.2 创建Chevereto的docker容器并运行
$ mkdir /appliaction
$ docker run -d \
--name=chevereto \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Asia/Shanghai \
-p 80:80 \
-p 443:443 \
-v /appliaction/chevereto/config:/config \
-v /appliaction/chevereto/data:/data \
--restart unless-stopped \
linuxserver/chevereto
2
3
4
5
6
7
8
9
10
11
12
命令说明:-e PUID=1000和-e PGID=1000指定用户和用户组ID,镜像官方的解释是,在用-v挂载卷的时候,主机操作系统和容器之间可能会出现权限问题,如果出现权限问题,可以指定这两个参数(不过一般情况下这俩不指定也能启动),-e TZ指定时区,两个-v挂载了配置文件和数据的目录。
# 3.3 添加反向代理并开启 HTTPS
使用OneinStack搭建Nginx并配置反向代理及HTTPS,具体操作步骤此处不再赘述。
# 3.4 执行Chevereto的安装过程
Chrome输入http://域名:端口号
路径,打开安装界面。
Step1:首先它会让你检查这两个路径与你要安装Chevereto的位置相匹配,并且没有安装其他软件。
Step2:然后填写数据库信息(Host处填写VPS的IP,其他项根据MySQL搭建时的配置来即可)
Step3:然后就是填写邮箱信息,可以用来重置密码什么的,填完邮箱就开始了自动安装,如果没有报错就算是初步搭建完成了。
# 3.5 使用Installer部署的注意事项
官方提供的Installer:Chevereto Installer (opens new window) Github项目地址:https://github.com/chevereto/installer (opens new window)
通过Installer部署需要安装PHP和Nginx环境,本文使用的installer.php是2.2.2版本的,PHP的最低支持是7.4,如果版本再低了无法安装。
将Installer上传至网站根目录,然后需要添加Nginx规则,规则放入Nginx配置里的server{}代码块中,并reload或restart nginx。
# Chevereto nginx generated rules
## Disable access to sensitive files
location ~* /(app|content|lib)/.*\.(po|php|lock|sql)$ {
deny all;
}
## CORS headers
location ~* /.*\.(ttf|ttc|otf|eot|woff|woff2|font.css|css|js) {
add_header Access-Control-Allow-Origin "*";
}
## Upload path for image content only and set 404 replacement
location ^~ /images/ {
location ~* (jpe?g|png|gif) {
log_not_found off;
error_page 404 /content/images/system/default/404.gif;
}
return 403;
}
## Pretty URLs
location / {
index index.php;
try_files $uri $uri/ /index.php?$query_string;
}
# END Chevereto nginx rules
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
注:如果不配置这个到installer安装的最后一步会出现“执行HTTP请求时出错”报错。上文所述的docker方式部署不用配这个,内置了。
后续与使用Docker部署的安装过程一致,不再赘述。
# 3.6 Chevereto基本设置及上传API
# 3.6.1 Chevereto基本设置
头像——仪表盘——设置——根据自己的需要进行配置,其中要注意的两点如下:
如果你的图床加载速度很慢,禁用自动更新可以有效缓解此问题(系统——“自动更新检查”、“显示可用的更新通知”设置成禁用)
如果你的图床只供给自己使用,请把开放注册的功能关掉(用户——“开放注册”设置成禁用)
# 3.6.2 获取Chevereto的上传API
头像——仪表盘——设置——API处,可以看到图片上传API的密钥,这个下面配置自动上传会用到。
该API的使用说明详见: Chevereto API v1 (opens new window),它的调用示例如下:
http://mysite.com/api/1/upload/?key=12345&source=http://somewebsite/someimage.jpg&format=json
API V1没有办法上传与给定用户相关联的图像,但是你可以覆盖默认的API,以实现以用户的形式上传:
打开网站根目录,将app/routes/route.api.php
文件复制到app/routes/overrides
文件夹,然后在此编辑该文件
将CHV\Image::uploadToWebsite($source);
改为CHV\Image::uploadToWebsite($source, '用户名');
(在第105行附近)
# 3.6.3 使用Postman测试Chevereto API
网络图片:上传网络图片使用GET或POST皆可。
GET http://mysite.com/api/1/upload/?key=12345&source=http://somewebsite/someimage.jpg&format=json
本地图片:上传本地文件时始终使用 POST。
POST http://mysite.com/api/1/upload/?key=12345&format=json // 在body的form-data处上传文件(key为source)
# 4. 配置图片自动上传及图片处理
# 4.1 使用Typora+Python脚本实现自动上传
我们可以利用Typora的Custom Command功能实现自动上传,这也是我目前写博客最常用的方案,效果很好,但不支持批量上传。
注:Typora需要是新版的,版本太老可能没有这个功能。Custom Command我这里用的是Python脚本,因此需要配置Python开发环境。
代码已在Github上开源,项目地址:https://github.com/Logistic98/upload-images (opens new window)
# 4.1.1 项目结构及代码
项目结构:
.
├── requirements.txt
└── upload-chevereto
├── config.ini
└── upload-chevereto.py
2
3
4
5
项目依赖:
requirements.txt
requests==2.27.1
项目代码:
./upload-chevereto/upload-chevereto.py
# -*- coding: utf-8 -*-
import json
import logging
import time
from configparser import ConfigParser
import requests
import argparse
import sys
# 读取配置文件
def read_config(config_path):
cfg = ConfigParser()
cfg.read(config_path, encoding='utf-8')
section_list = cfg.sections()
config_dict = {}
for section in section_list:
section_item = cfg.items(section)
for item in section_item:
config_dict[item[0]] = item[1]
return config_dict
# 按行读取文件的内容,保存成列表
def read_file_to_list(file_path):
result = []
with open(file_path, 'r') as f:
for line in f:
result.append(line.strip('\n'))
return result
# 获得本地图片路径后,上传至图床并记录返回的json字段
def up_to_chevereto(img_list):
for img in img_list:
# 先判断传过来的是本地路径还是远程图片地址
if "http" == img[:4]:
# 非本地图片的话可以考虑下载到本地再上传
logging.info(img)
continue
else:
try:
res_json = upload(img)
parse_response_url(res_json,img)
except Exception as e:
logging.error(e)
logging.error(img+"\tUpload failed")
# 调用 Chevereto API 上传图床
def upload(img):
files = {'source': open(img, "rb")}
# 拼接url并上传图片至Chevereto图床
url = config_dict['chevereto_url'] + "?key=" + config_dict['api_key'] + "&format=json"
r = requests.post(url, files=files)
return json.loads(r.text)
# 判断是否上传成功并记录日志
def parse_response_url(json, img_path):
# 从返回的json中解析字段
if json['status_code'] != 200:
logging.info(json['error'])
logging.info("{}\tupload failure. status_code {} .".format(
img_path, json['status_code'])
)
else:
img_url = json["image"]["url"]
# 上传成功的图片url必须用print(logger.info不可以)打印出来,Typora才能识别出是上传成功了,才会自动替换链接。
print(img_url)
# 记录上传成功的日志
upload_log_path = config_dict['upload_log_path']
with open(upload_log_path, 'a') as file_object:
upload_log_list = read_file_to_list(config_dict['upload_log_path'])
year = time.strftime("%Y", time.localtime())
if "## " + year not in upload_log_list:
file_object.write("## " + year + "\n\n")
month = time.strftime("%Y-%m", time.localtime())
if "## " + month not in upload_log_list:
file_object.write("## " + month + "\n\n")
file_object.write(img_url + " --" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "\n")
file_object.write("![](" + img_url + ")\n\n")
if __name__ == '__main__':
# 工具描述及帮助
TOOL_DESC = """
A command line tool to upload pictures to chevereto image bed
"""
logging.info(TOOL_DESC)
if len(sys.argv) == 1:
sys.argv.append('--help')
# 获取参数
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--source', type=str, nargs='+', help="上传图片路径", required=True)
parser.add_argument('-c', '--config', default="./config.ini", help="读取配置文件", required=True)
args = parser.parse_args()
img_list = args.source
config_path = args.config
# 读取配置文件
config_dict = read_config(config_path)
# 上传图片
up_to_chevereto(img_list)
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# 4.1.2 配置Typora的Custom Command功能
打开Typora——文件——偏好设置——图像
“插入图片时”处:选择“复制图片到
./${filename}.assets文件夹
”,勾选对本地位置和网络位置的图片应用上述规则”上传服务设定“处:上传服务选择
Custom Command
,命令填写为python upload-xxx.py的绝对路径 -c config.ini的绝对路径 -s
注:-s 后面是上传图片的路径,这个用的时候Typora就给我们自动补上了,不需要填写。
配置完毕可点击“验证图片上传选项"按钮进行验证,如果失败就要去检查你的INI配置文件、Python脚本文件和这里的命令是否配置正确.
配置成功后便可对md文档里的图片右键,使用上传图片功能了。上传成功后Typora会自动将我们的图片路径替换成链接,十分方便。
# 4.2 使用ShareX工具实现截图处理上传
# 4.2.1 下载安装ShareX工具
ShareX官网:https://getsharex.com/ (opens new window)
ShareX可以实现截图——处理截图——上传图床于一体,并提供上传后的URL,将原来的四步简化为两步。这个工具的功能远不止这些,很强大的一个工具,自行探索吧。
# 4.2.2 在ShareX里配置Chevereto API
打开ShareX——目标——上传目标设置——Chevereto——填写上传URL、API密钥,勾选直链,填写示例如下:
上传URL:http://example.com/api/1/upload
API密钥:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
2
注:上传URL的前半部分http://example.com
就是你的图床地址;后半部分/api/1/upload
是拼接成API的调用格式(upload的后面不要加/
),其中1是指上传用户的ID,这个根据实际填写(如果只有一个管理员用户,那这里就是1,不是的话就去看一下用户ID据实填写)
# 4.3 图片压缩并添加盲水印
图片在上传到图床之前,最好是对图片进行压缩,这样别人访问时,加载的时间就会缩短,提高访问速度。可以使用OpenCV来实现图片压缩。
$ pip install opencv-python
如果你想保护自己的原创图片,那最好的方式就是为图片添加盲水印,盲水印就是图片有水印但人眼看不出来,需要通过程序才能提取水印,相当于隐形“盖章”,可以用在数据泄露溯源、版权保护等场景。可以使用阿里巴巴安全团队出品的 blind_watermark (opens new window) 库对图片添加盲水印。
$ pip install blind-watermark
下面我写了一个Python脚本,可以对指定文件夹下的图片批量压缩并添加盲水印。
# -*- coding: utf-8 -*-
"""
# Features: 指定文件夹下的图片压缩并添加盲水印(替换原文件)
# Description: 图片压缩使用 opencv 实现,盲水印添加使用 blind_watermark 实现
# Dependcy: pip install opencv-python 、 pip install blind-watermark
# Demo:python compressAndWaterMark.py -s 'E:\inputDir'(支持一次执行多个目录,空格分隔即可)
"""
import os
import argparse
import re
import time
import cv2
from blind_watermark import WaterMark
"""
# 使用 opencv 实现图片压缩,compress_config为图片压缩配置,说明如下:
# [cv2.IMWRITE_PNG_COMPRESSION, 9] 无损压缩(取值范围:0~9,数值越小,压缩比越低)
# [cv2.IMWRITE_JPEG_QUALITY, 80] 有损压缩(取值范围:0~100,数值越小,压缩比越高,图片质量损失越严重)
"""
class Compress_img:
def __init__(self, img_path, compress_config):
self.img_path = img_path
self.img_name = img_path.split('/')[-1]
self.compress_config = compress_config
def compress_img_CV(self, show=False):
old_fsize = os.path.getsize(self.img_path)
# 读取并压缩图片
img_resize = cv2.imread(self.img_path)
cv2.imwrite(self.img_path, img_resize, self.compress_config)
new_fsize = os.path.getsize(self.img_path)
# 计算压缩率
compress_rate = str(round(new_fsize / old_fsize * 100, 2)) + "%"
print("%s Image is compressed." % (self.img_path), "compression ratio:", compress_rate)
# 查看压缩后的图片
if show:
cv2.imshow(self.img_name, img_resize)
cv2.waitKey(0)
"""
# 使用 blind_watermark 实现图片盲水印的添加
# 项目地址:https://github.com/guofei9987/blind_watermark
"""
class WaterMark_img:
def __init__(self, img_path, password, wm_text):
self.img_path = img_path
self.password = password
self.wm_text = wm_text
def add_waterMark(self):
# 设置密码并添加水印
bwm = WaterMark(password_img=self.password, password_wm=self.password)
bwm.read_img(self.img_path)
bwm.read_wm(self.wm_text, mode='str')
bwm.embed(self.img_path)
# 打印添加的水印
len_wm = len(bwm.wm_bit)
wm_extract = bwm.extract(self.img_path, wm_shape=len_wm, mode='str')
print("%s Blind watermark added, " % (self.img_path), "Content is:", wm_extract)
# 校验是否为可读取的图片文件(路径中不可有中文)
def check_valid_img(img_path):
zhmodel = re.compile(u'[\u4e00-\u9fa5]')
match = zhmodel.search(img_path)
if match:
print("The path cannot contain Chinese: " + img_path)
return False
try:
img_content = cv2.imread(img_path)
var = img_content[0]
return True
except:
print("The image file cannot be read: " + img_path)
return False
if __name__ == '__main__':
# 获取参数配置
parser = argparse.ArgumentParser()
parser.add_argument('-s', '--source', type=str, nargs='+', help="Specify the directory path", required=True)
parser.add_argument('-c', '--config', default=[cv2.IMWRITE_PNG_COMPRESSION, 9], help="Image compression config", required=False)
parser.add_argument('-p', '--password', default=123456, help="Blind Watermark Password", required=False)
parser.add_argument('-w', '--waterMark', default='@eula.club', help="Blind watermark text", required=False)
args = parser.parse_args()
source = args.source
compress_config = args.config
password = args.password
wm_text = args.waterMark
# 定义及初始化
start_time = time.time()
start_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
all_img_count = 0
success_img_count = 0
# 递归读取并进行处理
print("Start compressing pictures and adding blind watermarks, please wait...")
for dir_path in source:
file_dir_list = os.listdir(dir_path)
for img_name in file_dir_list:
img_path = dir_path + "/" + img_name
if(check_valid_img(img_path)):
all_img_count += 1
# 使用 blind_watermark 添加盲水印
try:
waterMark = WaterMark_img(img_path, password, wm_text)
waterMark.add_waterMark()
print("Blind watermarking succeeded, replaced original file: " + img_path)
except Exception:
print("Blind watermarking failed, the image has been skipped: " + img_path)
success_img_count += 1
# 使用 opencv 压缩图片
try:
compress = Compress_img(img_path, compress_config)
compress.compress_img_CV()
print("Compression succeeded, replaced original file: " + img_path)
except Exception:
print("Compression failed, the image has been skipped: " + img_path)
# 输出结果
end_time = time.time()
end_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
time_consuming_str = str((end_time - start_time) * 1000) + 'ms'
print("=====" + end_time_str + ": ")
print("A total of " + str(all_img_count) + " pictures were found")
print("Successfully processed " + str(success_img_count) + " images, total time: " + time_consuming_str)
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
注:OpenCV无法读取中文路径的图片,请使用全英文路径。
# 5. 参考资料
[1] Chevereto-搭建私人图片外链库 from Newlearnerの小站 (opens new window)
[2] Chevereto项目地址 from Github (opens new window)
[3] Chevereto-中文文档-上传到用户的解决方案 from Chevereto API (opens new window)
[4] 利用ShareX快速截图并自动上传分享到chevereto from 主机笔记 (opens new window)
[5] chevereto图床打开非常慢,谷歌浏览器显示为ping值慢 from pc6a (opens new window)
[6] 用docker快速搭建chevereto图床 from CSDN (opens new window)
[7] Chevereto API v1 使用说明 from Chevereto官方文档 (opens new window)
[8] 利用python脚本实现使用typora时图片自动上传到chevereto图床 from 知乎 (opens new window)
[9] ValueError: check_hostname requires server_hostname from Bilibili (opens new window)
[10] 令人不悦的Error--requests.exceptions.ProxyError from CSDN (opens new window)