需求来源

我在写博客的时候经常需要引用很多种格式的图片,比如 jpgjpegpnggif,这时候就出现一个问题,各种图片不仅格式不统一,而且很多图片非常大(因为有些图片是自己拍的,传输的时候是原图),但是放在博客上的图片这么大是没有意义的。网络在上传和分发图片的时候并不会按照原图来呈现,而且这样的清晰度对于网页浏览同样也资源过剩,反而造成了流量的浪费和用户的等待时间过长(除了一些摄影网站之类的可能需要原图)。

因此我必须解决图片格式不统一和图片过大的问题,这时候我就想能不能先在本地将图片压缩(最好是无损的),然后上传的时候直接上传压缩过后的图片就可以了,这样就能解决图片过大的问题。然后接下来的问题就是解决选择压缩到什么格式,经过一番探索,我选择压缩成 WebP格式

WebP 是一种新型图片格式,可为网络上的图片提供品质卓越的无损和有损压缩。使用 WebP,网站站长和网络开发者可以创建体积更小但细节更丰富的图片,从而提高网络访问速度。

WebP 无损图片比 PNG 图片小 26%。WebP 有损图片比采用等效 SSIM 质量指标的同等 JPEG 图片 缩小 25-34%

无损 WebP 支持透明度(也称为 Alpha 通道),只需 增加 22% 的字节。对于可以接受有损 RGB 压缩的情况,有损 WebP 也支持透明度,生成的文件大小通常比 PNG 小 3 倍。

动画 WebP 图片支持有损、无损和透明,与 GIF 和 APNG 相比,可以缩减文件大小。

选择WebP格式的原因除了支持很多格式图片的转换外,还因为它支持很多浏览器(IE浏览器用户就不管了,没有办法适配所有用户):

Google Chrome、Safari、Firefox、Edge、Opera 浏览器以及 许多其他工具和软件库都原生支持 WebP。开发者还添加了对各种图片编辑工具的支持。

WebP 包括轻量级编码和解码库 libwebp,以及用于将图片转换为 WebP 格式和从 WebP 格式转换为其他格式的命令行工具 cwebpdwebp,以及用于查看、混合和为 WebP 图片添加动画效果的工具。完整的源代码可在 下载页面上找到。

因此最终我打算将Hexo博客上的图片都转换为WebP格式。接下来将需求拆解:

  • 在本地安装WebP转换所需要的库 libwebp
  • 编写 PowerShell 脚本(我所使用的系统是 Windows 10)自动将其他格式的图片转换成 .webp 格式的图片
  • 编写新的 PowerShell 脚本修改 .md 的 Markdown 文件中引用图片的地方,将各种格式的图片后缀改成 .webp(因为我使用 Typora 来直接拖拽引入原图,所以需要在写完保存 .md 文件后再批量修改 .md 文件中引用图片的地方)
  • 将前面两个脚本整合到本地预览和远程部署的脚本当中

需求实现

安装WebP库

安装WebP转换相应的库,我采用 Scoop 进行安装:

1scoop install main/libwebp

安装完成之后通过下面的命令来验证是否安装成功:

1cwebp -version

编写自动化脚本

在Hexo博客目录下创建四个脚本分别用于执行不同的任务:

  • local.ps1 用于在本地预览最终的修改效果
  • deploy.ps1 用于部署到远程托管仓库
  • convert-to-webp.ps1 用于将本地的其他图片格式转换为 .webp 图片格式
  • update-markdown-images.ps1 用于更新 .md 文件中引用图片的地方

下面脚本的内容可以根据个人存放位置和需求的不同进行更改。

local.ps1

 1cd E:\ChenHuaneng\Article\Academic\academic-homepage
 2bundle exec jekyll build
 3robocopy "_site" "E:\ChenHuaneng\Article\Blogs\source\academic" /E /NFL /NDL /NP /XO
 4cd E:\ChenHuaneng\Article\Blogs
 5Write-Host "运行WebP转换..."
 6& .\convert-to-webp.ps1
 7Write-Host "更新Markdown图片引用..."
 8& .\update-markdown-images.ps1
 9npx hexo clean
10npx hexo g
11npx hexo s -p 8080

deploy.ps1

 1# 修改学术主页
 2Write-Host "修改学术主页..."
 3cd E:\ChenHuaneng\Article\Academic\academic-homepage
 4bundle exec jekyll build
 5robocopy "_site" "E:\ChenHuaneng\Article\Blogs\source\academic" /E /NFL /NDL /NP /XO
 6cd E:\ChenHuaneng\Article\Blogs
 7# 查看当前修改的文件
 8$gitStatus = git status --porcelain
 9$gitAhead = git status -b --porcelain | Select-String "ahead"
10
11# 判断是否有修改或者未推送的提交
12if ($gitStatus -or $gitAhead) {
13    Write-Host "检测到修改或未推送的提交,开始处理..."
14    
15    if ($gitStatus) {
16        # 添加所有修改的文件
17        git add .
18
19        # 提交修改,自动生成提交信息
20        $currentTime = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
21        $commitMessage = "Auto commit at $currentTime"
22        git commit -m $commitMessage
23    } else {
24        Write-Host "没有检测到新的修改,但有未推送的提交。"
25    }
26
27    # 推送到远程仓库
28    git push -u origin main
29} else {
30    Write-Host "没有检测到修改或未推送的提交,跳过 Git 操作。"
31}
32
33Write-Host "开始图片WebP转换..."
34Set-Location "E:\ChenHuaneng\Article\Blogs"
35& .\convert-to-webp.ps1
36Write-Host "WebP转换完成!"
37
38Write-Host "更新Markdown中的图片引用..."
39& .\update-markdown-images.ps1
40Write-Host "图片引用更新完成!"
41
42# 重新生成静态网页
43npx hexo clean
44npx hexo g
45
46# 部署更新后的网页文件
47hexo deploy

convert-to-webp.ps1

 1param(
 2    [string]$basePath = "E:\ChenHuaneng\Article\Blogs\source"
 3)
 4
 5# 支持的图片扩展名
 6$imageExtensions = @('.png','.jpg','.jpeg','.gif','.avif')
 7
 8# 需要处理的目录
 9$directories = @('imgs', '_posts', 'academic')
10
11# 获取cwebp完整路径 (避免环境变量问题)
12$cwebpPath = (Get-Command cwebp).Source
13
14foreach ($dir in $directories) {
15    $fullPath = Join-Path $basePath $dir
16    if (Test-Path $fullPath) {
17        Write-Host "Processing directory: $fullPath"
18        # 递归处理子目录中的图片
19        $files = Get-ChildItem -Path $fullPath -Recurse -File | Where-Object { $imageExtensions -contains $_.Extension.ToLower() }
20        
21        foreach ($file in $files) {
22            $webpPath = [System.IO.Path]::ChangeExtension($file.FullName, 'webp')
23            
24            # 仅当WebP文件不存在或比原图新时转换
25            if (-not (Test-Path $webpPath) -or $file.LastWriteTime -gt (Get-Item $webpPath).LastWriteTime) {
26                try {
27                    # 修复参数传递问题
28                    $qualityArg = "-q", "75"
29                    
30                    # 处理GIF的特殊情况
31                    if ($file.Extension -eq '.gif') {
32                        # GIF使用gif2webp工具处理
33                        $gif2webpPath = (Get-Command gif2webp).Source
34                        & $gif2webpPath "-q" "75" "-mixed" "$($file.FullName)" "-o" "$webpPath"
35                    }
36                    else {
37                        # 其他图片使用cwebp
38                        & $cwebpPath "-q" "75" "$($file.FullName)" "-o" "$webpPath"
39                    }
40                    
41                    if ($LASTEXITCODE -eq 0) {
42                        Write-Host "✅ Converted: $($file.Name)$([System.IO.Path]::GetFileName($webpPath))"
43                    } else {
44                        Write-Host "⚠️ Failed to convert: $($file.FullName) (Exit code: $LASTEXITCODE)"
45                    }
46                } catch {
47                    Write-Host "🛑 Error processing $($file.FullName): $_"
48                }
49            } else {
50                Write-Host "⏩ Skipped (up-to-date): $($file.Name)"
51            }
52        }
53    }
54}
55
56Write-Host "🎉 WebP conversion complete!"

update-markdown-images.ps1

 1# 参数定义:基础目录路径(可运行时指定)
 2param([string]$basePath = "E:\ChenHuaneng\Article\Blogs\source")
 3
 4# 定义目标子目录和支持的图片扩展名
 5$directories = @('_posts', 'image', 'about')
 6$imageExtensions = @('.png','.jpg','.jpeg','.gif','.avif')
 7
 8# 获取待处理的 Markdown 文件(3天内修改)
 9$markdownFiles = Get-ChildItem -Path $basePath -Recurse -Include "*.md" | 
10    Where-Object { 
11        $_.DirectoryName -match "($($directories -join '|'))" -and
12        $_.LastWriteTime -gt (Get-Date).AddDays(-3) 
13    }
14
15# 遍历每个文件进行替换
16foreach ($file in $markdownFiles) {
17    $content = Get-Content -Path $file.FullName -Raw
18    
19    # 正则表达式:匹配 Markdown 图片语法
20    $typoraPattern = '\!\[(.*?)\]\((.*?)(' + 
21        ($imageExtensions + $imageExtensions.ToUpper() | Join-String -Separator "|") + 
22        ')\)'
23    
24    # 执行替换(保留描述和路径,仅修改扩展名为 .webp)
25    $newContent = $content -replace $typoraPattern, '![$1]($2.webp)'
26    
27    # 保存修改后的文件
28    if ($newContent -ne $content) {
29        Set-Content -Path $file.FullName -Value $newContent -NoNewline
30        Write-Host "✅ Updated $($file.Name)"
31    } else {
32        Write-Host "⏩ No images to update in $($file.Name)"
33    }
34}
35Write-Host "🎉 All Markdown files updated to reference WebP images"