正式开始
请全程在Windows上操作
我们首先需要安装Scoop,这是一个适用于Windows的包管理器,个人认为非常好用
Scoop默认会安装到C盘,如果你想要换盘请按需更改
1 2 3 4
| $env:SCOOP='D:\Scoop' $env:SCOOP_GLOBAL='D:\ScoopApps' [Environment]::SetEnvironmentVariable('SCOOP', $env:SCOOP, 'User') [Environment]::SetEnvironmentVariable('SCOOP_GLOBAL', $env:SCOOP_GLOBAL, 'Machine')
|
安装Scoop:
1 2
| Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
|
如果你以管理员的身份会安装失败,请切换为普通用户。若想强制以管理员身份安装Scoop请使用
github原帖
出于安全考虑,默认情况下已禁用管理员控制台下的安装。如果您知道自己在做什么并希望以管理员身份安装Scoop,请下载安装程序并在提升的控制台中手动执行它,使用 -RunAsAdmin 参数。以下是示例:
1 2 3 4
| irm get.scoop.sh -outfile 'install.ps1' .\install.ps1 -RunAsAdmin [-OtherParameters ...]
iex "& {$(irm get.scoop.sh)} -RunAsAdmin"
|
安装Hugo框架:
然后选择一个你喜欢的文件夹创建你的站点。 myblog 即你的站点文件夹名称
1 2
| hugo new site myblog cd myblog
|
安装PaperMod主题:
1
| git clone https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod
|
站点根目录会有一个 hugo.toml。我推荐使用YAML。将文件重命名为 hugo.yaml。粘贴并更改以下内容
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
| baseURL: "https://站点url" title: "网站标题" LanguageCode: "zh-CN" theme: "PaperMod"
params: comments: false ShowCodeCopyButtons: true ShowBreadCrumbs: false ShowReadingTime: true ShowShareButtons: true disableThemeToggle: false assets: favicon: "/你的/网站图标.jpg" iconHeight: 35 homeInfoParams: Title: "首页展示的标题" Content: > 首页展示的文本
profileMode: enabled: false
label: text: "左上角显示的文本" icon: "/你的/左上角显示的图片.jpg" iconHeight: 35
socialIcons: - name: bilibili url: "" - name: github url: "" - name: telegram url: ""
menu: main: - identifier: categories name: 分类 url: /categories/ weight: 10 - identifier: tags name: 标签 url: /tags/ weight: 20 - identifier: archives name: 归档 url: /archives/ weight: 30 - identifier: search name: 搜索 url: /search/ weight: 40
outputs: home: - HTML - RSS - JSON
|
然后我们需要分别配置分类、标签、归档和搜索页
创建 content\categories\_index.md 写入:
1 2 3 4
| --- title: 分类 layout: categories ---
|
创建 content\tags\_index.md 写入:
1 2 3 4
| --- title: 标签 layout: tags ---
|
创建 content\archives.md 写入:
1 2 3 4
| --- title: 归档 layout: archives ---
|
创建 content\search.md 写入:
1 2 3 4
| --- title: "搜索" layout: "search" ---
|
然后我们要更改默认的文章创建模板
在 archetypes\default.md 写入:
1 2 3 4 5 6 7 8 9 10 11
| --- title: {{ replace .File.ContentBaseName "-" " " | title }} published: {{ .Date }} summary: "文章简介" cover: image: "文章封面图。也支持HTTPS" tags: [标签1, 标签2] categories: '文章所处的分类' draft: false lang: '' ---
|
接下来我们就可以通过命令来创建文章,并开始写作了。注意,最终构建的文章URL是你的文章的文件名。比如:https://你的网站.com/posts/first 所以文章文件名尽量简短,这并不会影响你的文章标题
当我们写完一篇文章想要预览网站,可以使用
当我们想要将站点发布到Vercel、Cloudflare Pages等静态网站托管平台可以将我们的 myblog 作为一个Git存储库提交到Github
根目录:./
输出目录:public
构建命令:hugo --gc
环境变量(适用于Vercel): Key:HUGO_VERSION Value:0.145.0
对象存储存图中间件代码:
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 214
| import 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_CONFIG = { 'account_id': '', 'access_key_id': '', 'secret_access_key': '', 'bucket_name': '' }
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 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() 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: 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("✅ 获取到剪贴板图片")
print("正在转换为 WebP 格式...") image_data = convert_to_webp(image) if not image_data: print("❌ 图片转换失败") return print(f"✅ 转换完成,大小: {len(image_data)/1024:.2f}KB")
print("正在上传到 R2...") filename = upload_to_r2(image_data) if not filename: print("❌ 上传失败") return print(f"✅ 上传成功,文件名: {filename}")
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()
|