使用Cloudflare R2 搭建免费图床
目录
我觉得CF是世界上最良心的网络公司,他提供的免费功能几乎可以完全覆盖一个普通人对于网络发布的需求,而且质量很稳定很好。
Cloudflare 配置
-
当然是先注册个Cloudflare账号,很简单,所以就略过了
-
登录CF后在左侧导航栏进入
存储和数据库-R2对象存储-概述
新用户的话会要求绑定支付账户,应为CF的存储桶每月免费流量是10G,对于做个博客来讲是足够用的,但是CF要求必须绑定支付协议才可以,以避免超量后没有支付。我是绑了Paypal,我的Paypal绑定的是一张交行的银联卡,里面木有钱,以防万一。
-
点击右上角
创建存储桶,起一个名字,要特别注意位置是不是亚太地区,如果不是,可以点击下面的提供位置提示(可选)选择亚太地区。
-
进入刚创建的存储桶,点击
设置,先设置一下自定义域,如果实在CF上面注册购买的域名,这里可以直接设置;如果不是,则需要先将你的域名托管到CF上面,再设置这里。
.r2.cloudflarestorage.com/xxx前面这段就是你的account_id,后面配置的时候会用得到。 -
回到
存储和数据库-R2对象存储-概述,右下角有API Tokens,点击Manage,进入后创建Account API 令牌,令牌名称可以自己起一个,权限选择对象读和写,其他的默认就可以,点击最下面的创建Account API 令牌。
-
注意创建令牌后不要关闭页面,这个页面只显示这一次,关闭后就再也不找不到ID了。

-
访问密钥 ID就是access_key_id -
机密访问密钥就是secret_access_key
Python 配置
- 脚本制作
- 新建文本文档,随便命个名(例如
r2),修改后缀为.py,用vscode打开。 - 复制下面的脚本粘贴进去:
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 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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214import keyboard import pyperclip from PIL import ImageGrab, Image import io import boto3 from botocore.config import Config import time import uuid import pyautogui import os from io import BytesIO # 示例配置 # # R2 配置 # R2_CONFIG = { # 'account_id': '你的account id', # 'access_key_id': '你的令牌key id', # 'secret_access_key': '你的令牌密码', # 'bucket_name': '桶名称' # } # # OSS 配置 # OSS_CONFIG = { # 'url': 'sb-eo-r2.2x.nz', # 'prefix': '/fuwari-blog/img' # } ######################################################### # R2 配置 R2_CONFIG = { 'account_id': '', 'access_key_id': '', 'secret_access_key': '', 'bucket_name': '' } # OSS 配置 OSS_CONFIG = { 'url': '你的自定义域', 'prefix': '存储路径' } ######################################################### def init_r2_client(): """初始化 R2 客户端""" return boto3.client( 's3', endpoint_url=f'https://{R2_CONFIG["account_id"]}.r2.cloudflarestorage.com', aws_access_key_id=R2_CONFIG['access_key_id'], aws_secret_access_key=R2_CONFIG['secret_access_key'], config=Config(signature_version='s3v4'), region_name='auto' ) def get_image_from_clipboard(): """从剪贴板获取图片""" try: image = ImageGrab.grabclipboard() if image is None: return None # 如果是列表(多个文件),取第一个 if isinstance(image, list): if len(image) > 0: # 如果是图片文件路径,打开它 try: return Image.open(image[0]) except Exception as e: print(f"打开图片文件失败: {e}") return None return None # 如果直接是 Image 对象 if isinstance(image, Image.Image): return image return None except Exception as e: print(f"获取剪贴板图片失败: {e}") return None def convert_to_webp(image): """将图片转换为 webp 格式""" if not image: return None try: buffer = BytesIO() # 确保图片是 RGB 模式 if image.mode in ('RGBA', 'LA'): background = Image.new('RGB', image.size, (255, 255, 255)) background.paste(image, mask=image.split()[-1]) image = background elif image.mode != 'RGB': image = image.convert('RGB') image.save(buffer, format="WEBP", quality=80) return buffer.getvalue() except Exception as e: print(f"转换图片失败: {e}") return None def upload_to_r2(image_data): """上传图片到 R2""" if not image_data: return None client = init_r2_client() # 生成基础文件名 base_filename = f"{uuid.uuid4()}.webp" filename = base_filename try: # 检查文件是否已存在 attempt = 1 while True: try: # 尝试获取文件信息,如果文件存在会返回数据,不存在会抛出异常 client.head_object( Bucket=R2_CONFIG['bucket_name'], Key=f"{OSS_CONFIG['prefix'].strip('/')}/{filename}" ) # 如果文件存在,修改文件名 name_without_ext = base_filename.rsplit('.', 1)[0] filename = f"{name_without_ext}_{attempt}.webp" attempt += 1 print(f"文件名已存在,尝试重命名为: {filename}") except client.exceptions.ClientError as e: # 如果是 404 错误,说明文件不存在,可以使用这个文件名 if e.response['Error']['Code'] == '404': break raise e # 其他错误则抛出 # 上传文件 client.put_object( Bucket=R2_CONFIG['bucket_name'], Key=f"{OSS_CONFIG['prefix'].strip('/')}/{filename}", Body=image_data, ContentType='image/webp' ) return filename except Exception as e: print(f"上传失败: {e}") return None def generate_markdown_link(filename): """生成 Markdown 图片链接""" if not filename: return None url = f"https://{OSS_CONFIG['url']}{OSS_CONFIG['prefix']}/{filename}" return f"" def type_markdown_link(markdown_link): """模拟键盘输入 Markdown 链接""" if not markdown_link: return pyperclip.copy(markdown_link) pyautogui.hotkey('ctrl', 'v') def handle_upload(): """处理图片上传的主函数""" print(f"\n[{time.strftime('%Y-%m-%d %H:%M:%S')}] 收到粘贴请求") print("正在检查剪贴板...") # 获取剪贴板图片 image = get_image_from_clipboard() if not image: print("❌ 剪贴板中没有图片") return print("✅ 获取到剪贴板图片") # 转换为 webp print("正在转换为 WebP 格式...") image_data = convert_to_webp(image) if not image_data: print("❌ 图片转换失败") return print(f"✅ 转换完成,大小: {len(image_data)/1024:.2f}KB") # 上传到 R2 print("正在上传到 R2...") filename = upload_to_r2(image_data) if not filename: print("❌ 上传失败") return print(f"✅ 上传成功,文件名: {filename}") # 生成并输入 Markdown 链接 markdown_link = generate_markdown_link(filename) if markdown_link: print(f"生成的 URL: https://{OSS_CONFIG['url']}{OSS_CONFIG['prefix']}/{filename}") print(f"模拟键入: {markdown_link}") type_markdown_link(markdown_link) print("✅ 操作完成") def main(): """主函数""" print("=" * 50) print("R2 图片上传插件已启动") print(f"当前配置:") print(f"- OSS 域名: {OSS_CONFIG['url']}") print(f"- 存储路径: {OSS_CONFIG['prefix']}") print(f"- R2 存储桶: {R2_CONFIG['bucket_name']}") print("使用 Ctrl+Alt+V 上传剪贴板中的图片") print("=" * 50) # 注册快捷键 keyboard.add_hotkey('ctrl+alt+v', handle_upload) # 保持程序运行 keyboard.wait() if __name__ == "__main__": main()- 编辑脚本中R2和OSS配置的内容

只需要编辑图中红框部分即可。
account_id:前文提到的.r2.cloudflarestorage.com/xxx前面部分。
access_key_id:前文提到的访问密钥 ID
secret_access_key:前文提到的机密访问密钥
bucket_name:你自己设置的桶名称
OSS里面的url: 你的自定义域,这个根据你前面设定的自定义域填写即可,不用写https//。
OSS里面的prefix:存储路径,可以自己定义,例如我的存储路径是/blog/img,这样上传的图片都会再这个存储桶的/blog/img/文件夹内。
- 新建文本文档,随便命个名(例如
- 下载安装最新版Python。Python官网,建议安装
Stable Releases里面的最新版本。

安装的是以后特别注意勾选Add Python to PATH,不然安装完了还得手动添加变量,怪麻烦的。
安装完了重启电脑
图床使用
- 将刚才配置的py脚本文件复制到需要的位置,例如我把我的脚本文件
r2.py放在我hugo网站主目录D:\hugo\buray下,在此目录下打开CMD,输入python r2.py回车运行。 - 会提示
ModuleNotFoundError,就是模块缺失提示,使用pip安装提示缺失的库即可,具体的命令为pip install xxxx。
例如第一个缺失模块提示一般是ModuleNotFoundError: No module named 'keyboard',那么就接着输入pip install keyboard进行安装,安装完成后再运行python r2.py,会提示第二个缺失模块,依次安装即可。
需要注意的是,期间会有一个提示PIL模块缺失,这个模块直接pip install PIL是安装不了的,因为PIL是一个简写,指的是基于Python的图像处理库,库名应该是pillow,安装这个库的时候使用pip install pillow,其他的缺失库都按照提示安装就行。 - 全部安装后运行
python r2.py会有如下提示:

- 使用的时候也很简单,确保py脚本运行状态后,再md文件中使用
Ctrl+Alt+V快捷键,图片就会自动上传到图床中,同时自动在你的md文件光标处输入图片链接:


这样就算大功告成了。
本文主要学习二叉树树大神的Hugo博客搭建教程以及配置调优