#!/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" </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" "$@"