文件
xiaozhongchuf/deploy/compress-uploads.sh
可乐加冰 0e26119bcf feat(deploy): 添加 uploads 图片批量压缩与一键恢复脚本
支持 2G+ 大批量安全压缩(65% 质量、最大宽 800px),含全量快照、校验与恢复能力。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-24 00:01:40 +08:00

124 行
3.7 KiB
Bash

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

#!/usr/bin/env bash
# 批量压缩 public/uploads 图片(2G+ 安全模式)
#
# 用法:
# bash deploy/compress-uploads.sh --dry-run # 预览
# bash deploy/compress-uploads.sh --safe # 推荐:rsync 快照 + 压缩
# bash deploy/compress-uploads.sh --safe --log=deploy/logs/compress.log
set -euo pipefail
DEPLOY_PATH="${DEPLOY_PATH:-$(cd "$(dirname "$0")/.." && pwd)}"
PHP_BIN="${PHP_BIN:-$(command -v php || true)}"
SCRIPT="${DEPLOY_PATH}/deploy/compress-uploads.php"
UPLOADS="${DEPLOY_PATH}/public/uploads"
SNAPSHOTS="${UPLOADS}/.compress_backup/snapshots"
LOG_DIR="${DEPLOY_PATH}/deploy/logs"
if [[ -z "$PHP_BIN" || ! -x "$PHP_BIN" ]]; then
echo "ERROR: 找不到可执行的 PHP,请设置 PHP_BIN"
exit 1
fi
if [[ ! -f "$SCRIPT" ]]; then
echo "ERROR: 脚本不存在: $SCRIPT"
exit 1
fi
if [[ ! -d "$UPLOADS" ]]; then
echo "ERROR: uploads 目录不存在: $UPLOADS"
exit 1
fi
# 解析 --safe 模式
SAFE_MODE=0
PASSTHRU=()
for arg in "$@"; do
if [[ "$arg" == "--safe" ]]; then
SAFE_MODE=1
else
PASSTHRU+=("$arg")
fi
done
echo "项目: ${DEPLOY_PATH}"
echo "PHP: ${PHP_BIN}"
echo ""
if [[ "$SAFE_MODE" -eq 1 ]]; then
SNAPSHOT_ID="$(date +%Y%m%d_%H%M%S)"
SNAPSHOT_DIR="${SNAPSHOTS}/${SNAPSHOT_ID}"
# 磁盘空间检查
UPLOADS_SIZE=$(du -sb "$UPLOADS" 2>/dev/null | awk '{print $1}' || echo 0)
FREE_SPACE=$(df -B1 "$UPLOADS" 2>/dev/null | awk 'NR==2{print $4}' || echo 0)
NEED=$(( UPLOADS_SIZE + UPLOADS_SIZE / 10 ))
echo "========== 安全模式 =========="
echo "uploads 大小: $(numfmt --to=iec "$UPLOADS_SIZE" 2>/dev/null || echo "${UPLOADS_SIZE}B")"
echo "磁盘可用: $(numfmt --to=iec "$FREE_SPACE" 2>/dev/null || echo "${FREE_SPACE}B")"
echo "快照 ID: ${SNAPSHOT_ID}"
echo ""
if [[ "$FREE_SPACE" -gt 0 && "$FREE_SPACE" -lt "$NEED" ]]; then
echo "ERROR: 磁盘空间不足,需要约 $(numfmt --to=iec "$NEED" 2>/dev/null || echo "${NEED}B")"
echo "请清理磁盘,或将 SNAPSHOTS 目录挂载到其他盘"
exit 1
fi
mkdir -p "$SNAPSHOT_DIR"
echo "正在创建全量快照(rsync,约 2G 需数分钟)..."
echo "快照目录: ${SNAPSHOT_DIR}"
echo ""
if command -v rsync >/dev/null 2>&1; then
rsync -a --info=progress2 \
--exclude='.compress_backup/' \
"${UPLOADS}/" "${SNAPSHOT_DIR}/"
else
echo "WARN: 未找到 rsync,使用 cp(较慢)"
find "$UPLOADS" -mindepth 1 -maxdepth 1 ! -name '.compress_backup' -exec cp -a {} "${SNAPSHOT_DIR}/" \;
fi
# 写入元数据
IMAGE_COUNT=$("$PHP_BIN" -r "
\$n=0; \$ext=['jpg','jpeg','png','webp','gif'];
\$it=new RecursiveIteratorIterator(new RecursiveDirectoryIterator('${UPLOADS}', FilesystemIterator::SKIP_DOTS));
foreach(\$it as \$f){ if(\$f->isFile() && in_array(strtolower(\$f->getExtension()),\$ext)) \$n++; }
echo \$n;
")
cat > "${SNAPSHOT_DIR}/.snapshot_meta.json" <<META
{
"created_at": "$(date -Iseconds 2>/dev/null || date)",
"image_count": ${IMAGE_COUNT},
"total_bytes": ${UPLOADS_SIZE},
"method": "rsync",
"max_width": 800,
"quality": 65
}
META
echo ""
echo "快照完成!"
echo "若压缩后有问题,一键恢复:"
echo " bash deploy/restore-uploads.sh --restore=${SNAPSHOT_ID}"
echo " bash deploy/restore-uploads.sh --latest"
echo ""
echo "========== 开始压缩 =========="
echo ""
mkdir -p "$LOG_DIR"
DEFAULT_LOG="${LOG_DIR}/compress_${SNAPSHOT_ID}.log"
HAS_LOG=0
for arg in "${PASSTHRU[@]}"; do
[[ "$arg" == --log=* ]] && HAS_LOG=1
done
if [[ "$HAS_LOG" -eq 0 ]]; then
PASSTHRU+=("--log=${DEFAULT_LOG}")
fi
# 已有快照,不再让 PHP 重复快照
exec "$PHP_BIN" -d memory_limit=512M "$SCRIPT" "${PASSTHRU[@]}"
fi
exec "$PHP_BIN" -d memory_limit=512M "$SCRIPT" "$@"