Obsidian => Cos => Hexo 同步部署
1. 安装 hexo 先安装 nodejs npm
1 2 3 4 5 6 7 8 9 10 11 12 # 安装 nodejs npm sudo apt update sudo apt install -y nodejs npm # nodejs npm 检查 node -v # v18.19.0 npm -v # 9.2.0 # npm 镜像 npm config set registry "https://registry.npmmirror.com/"
再安装 hexo
1 2 3 4 5 # 安装 Hexo 的 CLI 工具,使得可以使用 hexo 命令 npm install -g hexo-cli # hexo 检查 hexo v
hexo 初始化
1 2 3 4 5 6 7 8 9 10 11 12 # 创建目录,用来给 hexo 放博客文件,这里将 blog 文件夹作为博客目录 mkdir -p /opt/hexo/blog cd /opt/hexo/blog # 初始化 Hexo 项目,生成必要的目录和文件 hexo init # INFO Cloning hexo-starter https://github.com/hexojs/hexo-starter.git # INFO Install dependencies # INFO Start blogging with Hexo! # 安装 Hexo 项目依赖(此时 Hexo 会下载和安装一些依赖库) npm install
此时目录结构
1 2 3 4 5 6 7 8 9 10 ╰─❯ tree /opt/hexo/blog -L 1 /opt/hexo/blog ├── _config.landscape.yml ├── _config.yml ├── node_modules # 目录 ├── package.json ├── package-lock.json ├── scaffolds # 目录 ├── source # 目录 └── themes # 目录
2. Hexo 关键命令 这些命令,都需要在 /opt/hexo/blog 目录下执行,也就是第一次执行 hexo init
命令时,所在的目录
2.1. hexo server 生成静态文件 + 启动本地服务器
1 2 3 4 5 6 7 8 9 10 hexo server # INFO Validating config # INFO Start processing # INFO Hexo is running at http://localhost:4000/ . Press Ctrl+C to stop. # 可以简化为 hexo s # 可以加选项,显示更详细的内容,方便调试 hexo s --debug
效果如下(默认启动在 4000 端口启动)
2.2. hexo generate 仅生成静态文件,放在 public 下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 hexo generate source 下的所有 md 文件 这些 md 文件的 YAML Front Matter 元数据(标题、日期、标签等) hexo-renderer-marked 渲染器(package.json 中有写用了这个) public 下的静态资源文件: index.html 首页 categories/index.html 分类页 tags/index.html 标签页 2024/11/02/文章标题/index.html 文章页面 html css js images/图片 sitemap.xml rss.xml 等
命令效果
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 ╰─❯ tree /opt/hexo/blog/public /opt/hexo/blog/public ├── 2024 │ └── 11 │ └── 02 │ └── hello-world │ └── index.html ├── archives │ ├── 2024 │ │ ├── 11 │ │ │ └── index.html │ │ └── index.html │ └── index.html ├── css │ ├── images │ │ └── banner.jpg │ └── style.css ├── fancybox │ ├── jquery.fancybox.min.css │ └── jquery.fancybox.min.js ├── index.html └── js ├── jquery-3.6.4.min.js └── script.js 12 directories, 11 files
配置 nginx,访问 hexo generate 生成的页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 vim /etc/nginx/sites-available/hexo.conf server { listen 40000; server_name localhost; # 使用服务器的 IP 或域名替代 localhost(如有) root /opt/hexo/blog/public; # 替换为 Hexo public 目录的实际路径 index index.html; location / { try_files $uri $uri/ =404; } # 设置 gzip 压缩(可选,提高加载速度) gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; } ln -s /etc/nginx/sites-available/hexo.conf /etc/nginx/sites-enabled/ nginx -t && nginx -s reload
2.3. server VS generate
2.4. hexo clean 1 2 3 4 # 清除缓存 hexo clean db.json 等缓存文件 public 中的所有静态文件
2.5. hexo deploy 1 2 # 部署 Hexo 博客 hexo deploy
3. 多端同步 3.1. 先看最终效果 成功配置后,在 obsidian PC 端、移动端直接编辑 md 文件,然后使用插件同步 sync 完成后,博客就同步更新了 ✅
3.2. 云端配置 3.2.1. 存储桶 访问这个存储桶管理页面: https://console.cloud.tencent.com/cos/bucket 跟随页面引导,创建存储桶,成功后,得到访问域名,大概是像这样的格式:https://obsidian-blog-1234567890.cos.ap-shanghai.myqcloud.com
3.2.2. 子账号 创建子账号链接: https://console.cloud.tencent.com/cam/user/create 需要勾选“编程访问” 权限不用管 创建成功,保存好 id 和 key
3.2.3. 存储桶访问权限 让刚创建的子账号,有对应存储桶的权限
3.2.4. 存储桶信息整理 从域名中可以获得: BucketName: obsidian-blog-1234567890 Region: ap-shanghai Endpoint: cos.ap-shanghai.myqcloud.com
子账号: Access Key ID: AKID11111111111111aaaaaaaaaaaaaaaaaa Secret Access Key: 3333333331111111dddddBBBBBBBBBBB
3.3. obsidian 本地配置 Obsidian 中安装插件:Remotely Save 插件地址: https://github.com/remotely-save/remotely-save 插件配置: 检查是否成功: 现在 Obsidian 就可以利用插件,访问到云端存储桶了;移动端也一样,装了插件后,就可以查看、编辑、同步文件
3.4. hexo 服务器配置 3.4.1. s3cmd 配置 1 2 3 4 5 6 7 8 9 # 安装 apt install s3cmd -y # 用于配置 s3cmd,先一路回车,之后配置文件手动修改 s3cmd --configure # Save settings? [y/N] y # Configuration saved to '/root/.s3cfg' 这里提示生成的配置文件保存到 /root/.s3cfg 了
要修改的部分: 注:配置文件没看到存储桶名 bucketName 的配置,无所谓,之后用命令时要指定桶
Key
原内容
修改为
access_key
AKID11111111111111aaaaaaaaaaaaaaaaaa
access_token
3333333331111111dddddBBBBBBBBBBB
bucket_location
US
ap-shanghai
host_base
s3.amazonaws.com
cos.ap-shanghai.myqcloud.com
host_bucket
%(bucket)s.s3.amazonaws.com
%(bucket)s.cos.ap-shanghai.myqcloud.com
secret_key
3333333331111111dddddBBBBBBBBBBB
website_endpoint
http://%(bucket)s.s3-website-%(location)s.amazonaws.com/
http://%(bucket)s.cos-website-%(location)s.myqcloud.com/
3.4.2. s3cmd 使用 检查是否成功
1 s3cmd ls s3://obsidian-blog-1234567890
同步存储桶中内容到本地(会用 MD5 校验文件内容)
1 2 3 4 5 6 7 # 同步命令,方向是从左到右(云端 => 本地) s3cmd sync s3://obsidian-blog-1234567890/ /opt/obsidian/blog/ --delete-removed # 命令可选项 --skip-existing 不进行 MD5 校验,直接跳过目标已存在的文件 --delete-removed 删除目标中,多余的文件 --force
成功拉取到 cos 存储桶中的文件到本地
3.5. 目录结构
在 _posts 下建两个引用,指向 obsidian 下的对应目录
在 md 文档中,用这种语法引用图片


效果:
草稿不会被 hexo 感知到,也就不会发布
博客页面图片可以正常显示
3.6. hexo 自动拉取文件 这里暂时的解决方案是:写个 shell 脚本,脚本自己会按一定频率执行 s3cmd sync 命令,并且一直循环下去;然后用 pm2 管理、运行这个脚本 sync_s3.sh 脚本内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 # !/bin/bash while true; do echo "Starting sync at $(date)" # 执行同步命令 s3cmd sync s3://obsidian-blog-1234567890/ /opt/obsidian/blog/ --delete-removed # 输出同步结果的状态 if [ $? -eq 0 ]; then echo "Sync successful at $(date)" else echo "Sync failed at $(date)" fi # 输出一个换行,分隔每次同步的日志 echo "" # 每次同步后暂停1秒 sleep 1 done
run.js 内容
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 const { spawn } = require ('child_process' );const fs = require ('fs' );const path = require ('path' );const scriptPath = path.resolve ('/opt/s3cmd/sync_s3.sh' ); const logFilePath = path.resolve ('/opt/s3cmd/sync_log.txt' ); const logStream = fs.createWriteStream (logFilePath, { flags : 'a' }); const syncProcess = spawn (scriptPath, [], { stdio : ['ignore' , 'pipe' , 'pipe' ] }); syncProcess.stdout .pipe (logStream); syncProcess.stderr .pipe (logStream); syncProcess.on ('close' , (code ) => { console .log (`sync_s3.sh 进程退出,退出码: ${code} ` ); }); syncProcess.on ('error' , (err ) => { console .error (`启动 sync_s3.sh 时发生错误: ${err.message} ` ); });
pm2 管理
1 pm2 start /opt/s3cmd/run.js --name sync_s3
4. 其他操作 4.1. replace_thumbnail.py 目前保留的 2001 个 hello-word 文章,是为了提前暴露出性能问题,但看久了感觉封面不好看,所以整了个 python 脚本,去批量替换这些文章的封面,如下:
replace_thumbnail.py
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 import osimport remd_directory = r"E:\Study\obsidian\Blog\Blog\deploy" image_directory = r"E:\Study\obsidian\Blog\Blog\images\sao" image_files = [f for f in os.listdir(image_directory) if os.path.isfile(os.path.join(image_directory, f))] md_files = [f for f in os.listdir(md_directory) if f.startswith("hello-world" ) and f.endswith(".md" )] base_url = "https://blog.megumiai.com/images/sao/" pattern = re.compile (r'thumbnail: https://evan\.beee\.top/img/redefine-1-final\.webp' ) image_index = 0 for md_file in md_files: md_path = os.path.join(md_directory, md_file) with open (md_path, 'r' , encoding='utf-8' ) as file: content = file.read() if pattern.search(content): new_thumbnail = f"{base_url} {image_files[image_index]} " content = pattern.sub(f'thumbnail: {new_thumbnail} ' , content) with open (md_path, 'w' , encoding='utf-8' ) as file: file.write(content) image_index = (image_index + 1 ) % len (image_files) print ("已经完成所有 md 文件的替换操作。" )
4.2. fontawesome https://fontawesome.com/search
4.3. removebg https://www.remove.bg/zh/upload
4.4. convertio https://convertio.co/zh/
4.5. iloveimg https://www.iloveimg.com/zh-cn/compress-image/compress-jpg