前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >我是如何使用 Git 和腾讯云 Lighthouse 做图床,并使用 hook 实现 WebP 压缩与水印的?

我是如何使用 Git 和腾讯云 Lighthouse 做图床,并使用 hook 实现 WebP 压缩与水印的?

原创
作者头像
Mintimate
修改2024-11-25 11:46:22
修改2024-11-25 11:46:22
12810
代码可运行
举报
文章被收录于专栏:Mintimate's Blog
运行总次数:0
代码可运行

通常,我们的网站会将图片和网站“骨架”分开进行存储和“解耦”,方便使用 CDN 加速时,图片和网站可以使用不同的方案;也方便网站迁移。

不知道有没有小伙伴想过: 在自己的腾讯云轻量应用服务器上,部署 Git 服务端作为图床仓库,重新定向工作空间到网站目录,并使用 Git hook 实现图片的 WebP 压缩与水印?

头图,怎么能少?
头图,怎么能少?

这次就给大家浅浅分享一下。

Git

Git 相信大家都不陌生,它是一个开源的分布式版本控制系统,可以有效、高速地处理项目版本。Git 由 Linux 之父 Linus 于 2005 年创建,目前由 Git 软件基金会管理。

Git 的核心思想是分布式,推送内容使用 Hash 进行差异化校验,使得 Git 具有更高的可靠性和更快的速度。Git 还支持离线工作,这意味着你可以在没有网络连接的情况下进行开发,然后将其推送到远程仓库。也正是这个特性,使得 Git 成为了一个非常流行的版本控制系统。而我们,也可以使用这些特性,来创建一个图床。

舒服
舒服

想一想,本地存储图片,之后使用 Git 推送到远程仓库,是不是很方便呢?

如果是写文章,对于一些草稿图片,甚至还可以使用 Git 拉出分支,在文章完成后,合并主分支,统一上线。保持工作台干净,岂不美哉?

轻量应用服务器联合 Git 构建图床仓库
轻量应用服务器联合 Git 构建图床仓库

Bare 裸仓库

平时,我们使用 Git 时,一般会使用工作区,也就是我们平时写代码的地方。

工作区和版本控制区
工作区和版本控制区

进入.git 目录,就可以看到版本控制区:

Git 仓库
Git 仓库

但是,我们在腾讯云轻量应用服务器上部署的 Git 仓库,不需要工作空间给用户展示,只需要版本库,用于提供给客户端进行拉取和推送。所以,在服务器上,我们就可以使用裸仓库,作为图床仓库:

代码语言:bash
复制
# 创建一个裸仓库
git init --bare
裸仓库
裸仓库

hook 钩子

Git 提供了钩子机制,允许我们在 Git 仓库中添加自定义脚本,以在特定事件发生时执行。这些事件包括:提交、推送、合并、拉取等。钩子脚本可以在仓库的 .git/hooks 目录中找到。

我们可以使用这些钩子脚本,来执行一些自定义操作,比如: 重新定向工作空间,以及后续的图片压缩、水印等操作。

裸仓库同样提供了 hook 钩子。比如,我们可以在 hooks/post-receive 中,编写脚本,实现重新定向工作空间:

代码语言:bash
复制
#!/bin/zsh
# 重新定向工作空间,pathToWebSite 为网站目录,pathToBarePath 为裸仓库目录
git --work-tree=/pathToWebSite --git-dir=/pathToBarePath checkout -f

这样,本地推送图片到服务器上的 Git 裸仓库的流程就是:

推送到腾讯云轻量应用服务器的流程
推送到腾讯云轻量应用服务器的流程

既然 hook 是 shell 脚本,那么我们就可以使用 shell 脚本来实现图片的 WebP 压缩与水印了。

其实方法很多,比如: imagemagick,它是一个功能强大的图片处理工具,支持多种图片格式的转换,以及图片的裁剪、缩放、旋转、水印、滤镜等操作。

嘿嘿,看个人习惯啦
嘿嘿,看个人习惯啦

我更习惯用 python 的 Pillow 库,同样,它是一个强大的图像处理库,支持多种图片格式的转换。

WebP 格式

WebP 既支持有损压缩也支持无损压缩,相较于 PNG 格式,同样支持透明通道。在质量相同的情况下,WebP 也具有更小的文件体积。

WebP
WebP

所以,使用 WebP 格式,可以节省很间,进而提高网站内图片的加载速度; WebP 格式也支持渐进式加载,还可以进一步提高用户体验。

操作前提

本文的操作前提是什么呢?首先是要有一台 Linux 服务器,比如:我就是使用腾讯云的轻量应用服务器。腾讯云的轻量应用服务器(Lighthouse),性能足够强劲。

其实也可以买腾讯云的云服务器 (CVM) 的,选择 CVM 可以选配更强大的 CPU ,处理 WebP 压缩速度更快。

但是,我测试了一下,我使用的腾讯云轻量应用服务器,CPU 模拟型号是 Intel(R) Xeon(R) Gold 6133 CPU @ 2.50GHz,处理本文的 WebP 并行任务非常足够。

本次使用的演示服务器
本次使用的演示服务器

轻量应用服务器长期都有活动,可以进去探索一下:

软件方面,在腾讯云轻量应用服务器上,安装好 Git 和 Python:

代码语言:bash
复制
# 安装 Git
apt update && apt install git -y
# 安装 Python
apt install python3 python3-pip -y

与此同时,创建一个 Git 裸仓库,作为图床仓库:

代码语言:bash
复制
# 创建一个裸仓库
git init imageHost.git --bare
创建图床仓库
创建图床仓库

图片转 WebP

前文已经说到,使用 Pillow 库

Pillow 库支持多种图片格式的转换,包括:JPEGPNGGIFBMPTIFFPPMWebP 等。

按道理我们在腾讯云轻量应用服务器使用的是 Linux 镜像,是可以安装 Linux 的 imagemagick 库的。感兴趣的小伙伴可以尝试一下。

当然,其实你也可以使用存储桶的 WebP 转换功能,比如:腾讯云的 COS 存储桶。

腾讯云 COS WebP 转换
腾讯云 COS WebP 转换

其实我也有用过(大概 2021 年的时候?),改天有机会,给大家介绍一下。

Pillow 库

我们可以很方便地实现图片的 WebP 转换:

代码语言:python
代码运行次数:0
复制
from PIL import Image

def save_as_webp(image_target, target_path):
    """
    将图像保存为 WebP 格式,并进行优化设置.
    
    Args:
        image_target (PIL.Image.Image): 待保存的图像对象.
        target_path (str): 保存图像的目标路径.
    """
    # 保存图像文件为 WebP 格式
    # quality: 图像质量,范围0-100,越高质量越好,但文件越大
    # optimize: 启用无损优化,减小文件大小
    # lossless: 启用无损压缩,保证图像质量
    # method: 压缩方法,取值0-6,越大压缩比越大,但质量略有下降
    # save_all: 保存动画的所有帧
    # progressive: 启用渐进式显示
    image_target.save(target_path, 'webp', quality=80, optimize=True, lossless=False, method=6,
                        save_all=True,
                        progressive=True)

if __name__ == "__main__":
        # 假设图像对象为 image_obj
    image_obj = Image.open("path/to/image.jpg")
    
    # 定义保存路径
    target_path = "path/to/save/image.webp"
    
    # 保存为 WebP 格式
    save_as_webp(image_obj, target_path)

添加水印

Pillow 库同样支持添加水印,只需要把水印图片“贴”到目标图片上即可:

代码语言:python
代码运行次数:0
复制
from PIL import Image, ImageDraw, ImageFont

def add_watermark(input_path, watermark_path, output_path):
    """
    在图像上添加水印.
    
    Args:
        input_path (str): 原始图像的路径.
        watermark_path (str): 水印图像的路径.
        output_path (str): 添加水印后的图像保存路径.
    """
    # 打开原始图像
    image = Image.open(input_path)
    
    # 打开水印图像
    watermark = Image.open(watermark_path)
    
    # 获取原始图像和水印图像的尺寸
    image_width, image_height = image.size
    watermark_width, watermark_height = watermark.size
    
    # 计算水印的位置,置于右下角
    x = image_width - watermark_width - 20
    y = image_height - watermark_height - 20
    
    # 将水印图像叠加到原始图像上
    image.paste(watermark, (x, y), watermark)
    
    # 保存结果图像
    image.save(output_path)

# 示例用法
add_watermark("path/to/image.jpg", "path/to/watermark.png", "path/to/output.jpg")

一般,水印的位置在图片的下方,并且水印需要设置透明度,在图片的尺寸过小时候,取消水印:

代码语言:python
代码运行次数:0
复制
with Image.open(input_path) as image:
    original_size = os.path.getsize(input_path)
    # 计算水印的放置位置以底部居中
    bg_width, bg_height = image.size
    wm_width, wm_height = wm_img.size
    position = ((bg_width - wm_width) // 2, bg_height - wm_height - 30)  # 底部居中坐标

    # 创建一个新的透明图层用于合并,以防背景颜色受影响
    image_target = Image.new('RGBA', image.size, (255, 255, 255, 0))  # 完全透明图层
    image_target.paste(image, (0, 0))  # 将背景图片粘贴到透明图层上

    # 粘贴水印到新图层的底部中心位置,透明度已由水印图片自身定义,无需额外调整
    if bg_width > 512 * 1.5 and watermark_mode:
        image_target.paste(wm_img, position, mask=wm_img.split()[3])  # 使用alpha通道作为遮罩确保透明度正确

最终的流程就是:

当前的处理流程
当前的处理流程

本文章内的图片,就是这样转换的。

Git 差异化文件

Git 作为版本控制工具,可以很方便地实现差异化文件检录,可能平时大家用习惯了 JetBrains、GitHub Desktop 等工具,但是,Git 的命令行强大到可怕。

我们如果要实现两个 commit 的差异化文件,只需要使用 git diff 命令即可:

代码语言:bash
复制
# 比较两个 commit 的差异
git diff <commit-id> <commit-id>

比如:比较 Hash 值为6abbb89127c806928666b12374dfa013ef95f8b8和 Hash 值为5fe2590034f4922c427ab74bf948af46c2627d99的两个 commit 的差异:

git diff
git diff

是不是有点抽象? 这里我们可以使用类似的命令 git difftree 并追加一下参数:

  • --no-commit-id:输出的差异信息中不包含提交 ID;
  • --name-status:仅显示文件名和状态(如添加、修改、删除等),而不显示具体的差异内容;
  • -r:递归地比较两个树中的所有子树。

这样的结果就豁然开朗了:

git difftree
git difftree

这个时候,如果两次 commit 存在差异,那么可能的输出结果就是:

代码语言:txt
复制
# 修改文件
M modified_file.png
# 添加文件
A new_file.png
# 删除文件
D deleted_file.jpg
# 重命名文件、移动文件
R old_name.png new_name.pg

发现了什么?🤔 没错,我们在 shell 内,可以使用标准化输出的方式,格式化参数文件。

很丝滑
很丝滑

hook 实现

现在,我们看看如何使用 hook 实现自动化部署。首先是工作分区的重定向和参数的定义:

代码语言:bash
复制
# 定义图片文件后缀(需要转换为 WebP 格式的文件后缀)
image_extensions=(".png" ".jpg" ".jpeg" ".PNG")

# 网站目标目录
WEB_DIR="/www/webRoot/imagehost.mintimate.cn"
# 工作空间临时检录目录
WORK_SPACE_DIR="/home/git/mySpace/imagehost.mintimate.cn"
# 定义需要跳过的文件前缀
skip_prefixes=("emoticon" "emoji")
# Python Fle Path
PYTHON_MAIN="/home/git/PythonTool"
# 检录工作空间到目标目录
git --work-tree=$WORK_SPACE_DIR --git-dir=/home/git/mySource/imageHost.git checkout -f

因为,我们提交内容的时候,难免会提交一些无关紧要的文件,比如:表情包、emoji 等,所以,我们需要过滤掉这些文件,并定义需要转换的文件后缀以及需要跳过的文件前缀,配合函数完成逻辑判断:

代码语言:bash
复制
# 定义一个函数,用于检查文件路径是否以需要跳过的前缀开头
check_skip_prefix() {
    local filepath=$1
    for prefix in "${skip_prefixes[@]}"; do
        if [[ "$filepath" == "$prefix"* ]]; then
            return 0  # 返回 0 表示匹配到了需要跳过的前缀
        fi
    done
    return 1  # 返回 1 表示没有匹配到需要跳过的前缀
}

# 定义一个函数,用于检查文件是否为图片
is_image_file() {
    local filepath=$1
    for ext in "${image_extensions[@]}"; do
        if [[ "$filepath" == *"$ext" ]]; then
            return 0  # 返回 0 表示是图片文件
        fi
    done
    return 1  # 返回 1 表示不是图片文件
}

定义日志输出目录:

代码语言:bash
复制
# 定义输出目录
OUTPUT_DIR="/home/git/runLog"
# 确保输出目录存在
mkdir -p $OUTPUT_DIR

# 获取当前日期和时间,格式为 YYYYMMDD-HHMMSS
NOW=$(date +"%Y%m%d-%H%M%S")
# 定义输出文件,包含时间戳
OUTPUT_FILE="${OUTPUT_DIR}/${NOW}_Change.log"

之后,标准化我们上文使用 git difftree 命令的输出:

代码语言:bash
复制
# 读取标准输入(oldrev newrev refname)
while read oldrev newrev refname
do
    # 获取变更的文件列表
    echo "Changes in $refname:" >> $OUTPUT_FILE
    # 使用 git diff-tree 来查看变更
    git diff-tree --no-commit-id --name-status -r $oldrev $newrev | while read status_flag file1 file2
    do
        case $status_flag in
            M|A)
                echo "Modify: $file1" >> $OUTPUT_FILE
                ;;
            D)
                echo "Delete: $file1" >> $OUTPUT_FILE
                ;;
            R)
                echo "MV $file1 To $file2" >> $OUTPUT_FILE
                ;;
        esac
    done
done

最后,对文件进行二次读取,判断是否需要使用 Python 脚本进行转换:

代码语言:bash
复制
# 使用 case 语句处理不同的操作
while read line; do
    case $line in
        Modify*)
            filepath=$(echo $line | awk '{print $2}')
            process_file "$filepath" modify
            ;;
        Delete*)
            filepath=$(echo $line | awk '{print $2}')
            rm -f "$WEB_DIR/$filepath"
            rm -f "$WEB_DIR/${filepath%.*}.webp"
            ;;
        MV*)
            src=$(echo $line | awk '{print $2}')
            dst=$(echo $line | awk '{print $3}')
            rm -f "$WEB_DIR/$src"
            rm -f "$WEB_DIR/${src%.*}.webp"
            process_file "$dst" move
            ;;
    esac
done < $OUTPUT_FILE

当然,process_file 函数的实现就比较简单了,直接调用 Python 脚本即可:

代码语言:bash
复制
# 使用 Python WebP解析脚本
process_file() {
    local filepath=$1
    local action=$2

    # 判断是否存在上级目标目录
    mkdir -p "$(dirname "$WEB_DIR/$filepath")"

    # 检查文件路径是否以需要跳过的前缀开头
    check_skip_prefix "$filepath"
    if [ $? -eq 0 ]; then
        cp "$WORK_SPACE_DIR/$filepath" "$WEB_DIR/$filepath"
        return
    fi

    # 检查文件是否为图片
    is_image_file "$filepath"
    if [ $? -eq 0 ]; then
        # 执行 python 脚本
        nohup $PYTHON_MAIN/bin/python $PYTHON_MAIN/image2WebpForGit.py -w -s "$WORK_SPACE_DIR/$filepath" -t "$WEB_DIR/${filepath%.*}.webp" >> $OUTPUT_FILE_PY 2>&1 &
    else
        # 如果不是图片,执行 cp 命令
        cp "$WORK_SPACE_DIR/$filepath" "$WEB_DIR/$filepath"
    fi
}

需要注意,这里我预留了 action 参数,但是我没有使用。

最终效果

最后,我们来看看效果,经过 commit 和 push 操作推送到我们自己的 Git 仓库之后,进而存储到腾讯云轻量应用服务器 Linux 的硬盘存储内,可以查看日志:

查看 Git Hook 操作日志
查看 Git Hook 操作日志

与此同时,我们也可以在 web 目录下查看转换后的图片:

查看 Git Hook 转换后的图片
查看 Git Hook 转换后的图片

看看转换后的图片和原始图片的大小对比:

查看 Git Hook 转换后的图片大小
查看 Git Hook 转换后的图片大小

同时,本篇文章内的图片,也是使用上述方法转换的,

对于性能的消耗,也是微乎其微的,我们可以在腾讯云轻量应用服务器的后台,看到性能监控曲线:

腾讯云轻量应用服务器性能监控曲线
腾讯云轻量应用服务器性能监控曲线

END

好啦,本篇文章就到这里,感谢阅读。之后的步骤,就看每个人的想法了。比如我就是使用 Nginx 作为反向代理,将转换后的图片直接返回给用户,这样就可以减少服务器的负担了。

有时候也会套一层 CDN,这样就可以加速图片的访问了。

相关代码已经开源,可以访问 https://github.com/Mintimate/GitHookPng2WebP 查看。

如果你觉得本篇教程对你有帮助,欢迎在B站、知乎、公众号、博客园等平台分享给更多人。一起交流学习,共同进步。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Git
    • Bare 裸仓库
    • hook 钩子
  • WebP 格式
  • 操作前提
  • 图片转 WebP
    • Pillow 库
    • 添加水印
  • Git 差异化文件
  • hook 实现
  • 最终效果
  • END
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档