fix: 修复博客图片加载失败(掘金防盗链)
- 将《Git使用教学》中的掘金外链图片下载至 source/images/ 并替换为站内路径 - _config.yml 注入 no-referrer meta 作为兜底方案 - 新增 tools/download-juejin-images.js 批量下载/替换脚本
This commit is contained in:
+122
@@ -0,0 +1,122 @@
|
||||
param(
|
||||
# 要执行的命令:help / clean / build / server / preview / deploy
|
||||
[Parameter(Position=0)]
|
||||
[ValidateSet('help','clean','build','server','preview','deploy')]
|
||||
[string]$Command = 'help'
|
||||
)
|
||||
|
||||
# 只要出现错误就立即停止,避免后续命令继续执行
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
# 以脚本所在目录为基准,切回博客根目录
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $root
|
||||
|
||||
# 环境变量可覆盖默认值
|
||||
$port = if ($env:PORT) { $env:PORT } else { '4000' }
|
||||
$hostName = if ($env:HOST) { $env:HOST } else { '127.0.0.1' }
|
||||
$branch = if ($env:BRANCH) { $env:BRANCH } else { 'main' }
|
||||
|
||||
function Show-Usage {
|
||||
@"
|
||||
博客脚本用法:
|
||||
./tools/blog.ps1 clean # 清理缓存和 public 目录
|
||||
./tools/blog.ps1 build # 生成静态文件
|
||||
./tools/blog.ps1 server # 启动本地服务
|
||||
./tools/blog.ps1 preview # 清理 + 生成 + 启动预览
|
||||
./tools/blog.ps1 deploy # 清理 + 生成 + git 提交 + 推送
|
||||
|
||||
可选环境变量:
|
||||
PORT=4000 预览端口
|
||||
HOST=127.0.0.1 监听地址
|
||||
BRANCH=main 推送分支
|
||||
"@
|
||||
}
|
||||
|
||||
function Invoke-Hexo {
|
||||
# 把后续参数原样转发给 hexo
|
||||
param([Parameter(ValueFromRemainingArguments = $true)][string[]]$HexoArgs)
|
||||
|
||||
& npx hexo @HexoArgs
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
}
|
||||
|
||||
function Add-BlogFiles {
|
||||
# 只暂存博客相关文件,避免误把 node_modules、public 或其他杂项提交进去
|
||||
$paths = @(
|
||||
'_config.yml',
|
||||
'_config.butterfly.yml',
|
||||
'_config.landscape.yml',
|
||||
'package.json',
|
||||
'package-lock.json',
|
||||
'scaffolds',
|
||||
'source',
|
||||
'themes',
|
||||
'tools'
|
||||
)
|
||||
|
||||
$existingPaths = @()
|
||||
foreach ($path in $paths) {
|
||||
if (Test-Path $path) {
|
||||
$existingPaths += $path
|
||||
}
|
||||
}
|
||||
|
||||
if ($existingPaths.Count -eq 0) {
|
||||
Write-Host '[deploy] 没有找到可暂存的博客文件'
|
||||
exit 1
|
||||
}
|
||||
|
||||
git add -- @existingPaths
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
}
|
||||
|
||||
switch ($Command) {
|
||||
'clean' {
|
||||
Write-Host '[clean] 清理生成缓存'
|
||||
Invoke-Hexo clean
|
||||
}
|
||||
'build' {
|
||||
Write-Host '[build] 生成静态文件'
|
||||
Invoke-Hexo generate
|
||||
}
|
||||
'server' {
|
||||
Write-Host "[server] 启动预览服务:http://${hostName}:${port}"
|
||||
Invoke-Hexo server -i $hostName -p $port
|
||||
}
|
||||
'preview' {
|
||||
Write-Host '[preview] 清理并生成静态文件'
|
||||
Invoke-Hexo clean
|
||||
Invoke-Hexo generate
|
||||
|
||||
Write-Host "[preview] 启动预览服务:http://${hostName}:${port}"
|
||||
Invoke-Hexo server -i $hostName -p $port
|
||||
}
|
||||
'deploy' {
|
||||
Write-Host '[deploy] 清理并生成静态文件'
|
||||
Invoke-Hexo clean
|
||||
Invoke-Hexo generate
|
||||
|
||||
Write-Host '[deploy] 只暂存博客相关改动'
|
||||
Add-BlogFiles
|
||||
|
||||
# 没有变更就不提交也不推送
|
||||
git diff --cached --quiet
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host '[deploy] 没有可提交的改动,跳过 commit/push'
|
||||
exit 0
|
||||
}
|
||||
|
||||
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
|
||||
Write-Host "[deploy] 提交改动:chore: deploy blog $timestamp"
|
||||
git commit -m "chore: deploy blog $timestamp"
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
Write-Host "[deploy] 推送到 origin/$branch"
|
||||
git push origin $branch
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
}
|
||||
default {
|
||||
Show-Usage
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# 切换到博客根目录,确保从任何位置执行都能正常工作
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT_DIR"
|
||||
|
||||
# 允许用环境变量覆盖默认值
|
||||
PORT="${PORT:-4000}"
|
||||
HOST="${HOST:-127.0.0.1}"
|
||||
BRANCH="${BRANCH:-main}"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
博客脚本用法:
|
||||
./tools/blog.sh clean # 清理缓存和 public 目录
|
||||
./tools/blog.sh build # 生成静态文件
|
||||
./tools/blog.sh server # 启动本地服务
|
||||
./tools/blog.sh preview # 清理 + 生成 + 启动预览
|
||||
./tools/blog.sh deploy # 清理 + 生成 + git 提交 + 推送
|
||||
|
||||
可选环境变量:
|
||||
PORT=4000 预览端口
|
||||
HOST=127.0.0.1 监听地址
|
||||
BRANCH=main 推送分支
|
||||
EOF
|
||||
}
|
||||
|
||||
hexo_run() {
|
||||
# 把参数原样传给 hexo
|
||||
npx hexo "$@"
|
||||
}
|
||||
|
||||
add_blog_files() {
|
||||
# 只暂存博客相关文件,避免误把 node_modules、public 或其他杂项提交进去
|
||||
local paths=(
|
||||
"_config.yml"
|
||||
"_config.butterfly.yml"
|
||||
"_config.landscape.yml"
|
||||
"package.json"
|
||||
"package-lock.json"
|
||||
"scaffolds"
|
||||
"source"
|
||||
"themes"
|
||||
"tools"
|
||||
)
|
||||
|
||||
local existing=()
|
||||
local path
|
||||
for path in "${paths[@]}"; do
|
||||
if [[ -e "$path" ]]; then
|
||||
existing+=("$path")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#existing[@]} -eq 0 ]]; then
|
||||
echo '[deploy] 没有找到可暂存的博客文件' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git add -- "${existing[@]}"
|
||||
}
|
||||
|
||||
case "${1:-help}" in
|
||||
clean)
|
||||
echo '[clean] 清理生成缓存'
|
||||
hexo_run clean
|
||||
;;
|
||||
build)
|
||||
echo '[build] 生成静态文件'
|
||||
hexo_run generate
|
||||
;;
|
||||
server)
|
||||
echo "[server] 启动预览服务:http://${HOST}:${PORT}"
|
||||
hexo_run server -i "$HOST" -p "$PORT"
|
||||
;;
|
||||
preview)
|
||||
echo '[preview] 清理并生成静态文件'
|
||||
hexo_run clean
|
||||
hexo_run generate
|
||||
|
||||
echo "[preview] 启动预览服务:http://${HOST}:${PORT}"
|
||||
hexo_run server -i "$HOST" -p "$PORT"
|
||||
;;
|
||||
deploy)
|
||||
echo '[deploy] 清理并生成静态文件'
|
||||
hexo_run clean
|
||||
hexo_run generate
|
||||
|
||||
echo '[deploy] 只暂存博客相关改动'
|
||||
add_blog_files
|
||||
|
||||
# 没有变更就不提交也不推送
|
||||
if git diff --cached --quiet; then
|
||||
echo '[deploy] 没有可提交的改动,跳过 commit/push'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
COMMIT_MSG="chore: deploy blog $(date '+%F %T')"
|
||||
echo "[deploy] 提交改动:${COMMIT_MSG}"
|
||||
git commit -m "$COMMIT_MSG"
|
||||
|
||||
echo "[deploy] 推送到 origin/${BRANCH}"
|
||||
git push origin "$BRANCH"
|
||||
;;
|
||||
help|-h|--help)
|
||||
usage
|
||||
;;
|
||||
*)
|
||||
echo "未知命令: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,74 @@
|
||||
// 下载 Markdown 中的掘金外链图片到 source/images/,并替换链接为站内路径
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
|
||||
const ROOT = 'c:/Users/Administrator/Desktop/新建文件夹/blog';
|
||||
const MD = path.join(ROOT, 'source/_posts/Git使用教学.md');
|
||||
const OUT = path.join(ROOT, 'source/images');
|
||||
|
||||
function download(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const lib = url.startsWith('https') ? https : http;
|
||||
const req = lib.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }, (res) => {
|
||||
// 跟随重定向
|
||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
res.resume();
|
||||
return download(new URL(res.headers.location, url).href).then(resolve, reject);
|
||||
}
|
||||
if (res.statusCode !== 200) {
|
||||
res.resume();
|
||||
return reject(new Error('HTTP ' + res.statusCode));
|
||||
}
|
||||
const chunks = [];
|
||||
res.on('data', (c) => chunks.push(c));
|
||||
res.on('end', () => resolve(Buffer.concat(chunks)));
|
||||
});
|
||||
req.on('error', reject);
|
||||
req.setTimeout(30000, () => req.destroy(new Error('timeout')));
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
fs.mkdirSync(OUT, { recursive: true });
|
||||
let content = fs.readFileSync(MD, 'utf8');
|
||||
|
||||
const re = /!\[[^\]]*\]\((https:\/\/p3-juejin\.byteimg\.com\/[^)\s]+)\)/g;
|
||||
const map = {}; // url -> /images/xxx.webp
|
||||
let m;
|
||||
const tasks = [];
|
||||
|
||||
while ((m = re.exec(content)) !== null) {
|
||||
const url = m[1];
|
||||
if (map[url]) continue;
|
||||
const seg = url.split('/').pop();
|
||||
const hash = seg.split('~')[0];
|
||||
if (!hash) {
|
||||
console.log('跳过(无法解析文件名): ' + url);
|
||||
continue;
|
||||
}
|
||||
const file = hash + '.webp';
|
||||
map[url] = '/images/' + file;
|
||||
tasks.push(
|
||||
download(url)
|
||||
.then((buf) => {
|
||||
fs.writeFileSync(path.join(OUT, file), buf);
|
||||
console.log('[OK] ' + file + ' <- ' + url);
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log('[FAIL] ' + url + ' -> ' + e.message);
|
||||
delete map[url];
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(tasks);
|
||||
|
||||
for (const url in map) {
|
||||
content = content.split(url).join(map[url]);
|
||||
}
|
||||
fs.writeFileSync(MD, content); // UTF-8 无 BOM
|
||||
|
||||
console.log('\n完成:处理 ' + Object.keys(map).length + ' 张图片');
|
||||
})();
|
||||
@@ -0,0 +1,61 @@
|
||||
# 下载 Markdown 中的掘金外链图片到 source/images/,并替换链接为站内路径
|
||||
param(
|
||||
[string]$MdPath = "c:/Users/Administrator/Desktop/新建文件夹/blog/source/_posts/Git使用教学.md",
|
||||
[string]$OutDir = "c:/Users/Administrator/Desktop/新建文件夹/blog/source/images"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
$content = Get-Content -Path $MdPath -Raw -Encoding UTF8
|
||||
|
||||
# 匹配  中的掘金图片地址
|
||||
$pattern = '!\[[^\]]*\]\((https://p3-juejin\.byteimg\.com/[^)\s]+)\)'
|
||||
$imgMatches = [regex]::Matches($content, $pattern)
|
||||
|
||||
$map = @{} # url -> /images/xxx.webp
|
||||
$count = 0
|
||||
$failed = @()
|
||||
|
||||
foreach ($m in $imgMatches) {
|
||||
$url = $m.Groups[1].Value
|
||||
if ($map.ContainsKey($url)) { continue }
|
||||
|
||||
# 用 URL 路径最后一段中 "~" 之前的部分作为文件名(即图片 hash)
|
||||
$seg = ($url -split '/')[-1]
|
||||
$hash = ($seg -split '~')[0]
|
||||
if ([string]::IsNullOrEmpty($hash)) {
|
||||
$failed += "无法解析文件名: $url"
|
||||
continue
|
||||
}
|
||||
$file = $hash + '.webp'
|
||||
$dest = Join-Path $OutDir $file
|
||||
|
||||
try {
|
||||
curl.exe -s -L --max-time 30 -o $dest $url
|
||||
if ((Test-Path $dest) -and ((Get-Item $dest).Length -gt 0)) {
|
||||
$map[$url] = '/images/' + $file
|
||||
$count++
|
||||
Write-Host "[OK] $file <- $url"
|
||||
} else {
|
||||
$failed += "下载为空: $url"
|
||||
}
|
||||
} catch {
|
||||
$failed += "下载异常: $url -> $_"
|
||||
}
|
||||
}
|
||||
|
||||
# 替换 Markdown 中的外链
|
||||
foreach ($kv in $map.GetEnumerator()) {
|
||||
$content = $content.Replace($kv.Key, $kv.Value)
|
||||
}
|
||||
|
||||
# 写回(UTF-8 无 BOM)
|
||||
[System.IO.File]::WriteAllText($MdPath, $content, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "完成:成功下载 $count 张,失败 $($failed.Count) 张"
|
||||
if ($failed.Count -gt 0) {
|
||||
Write-Host "失败列表:"
|
||||
$failed | ForEach-Object { Write-Host " - $_" }
|
||||
}
|
||||
Reference in New Issue
Block a user