#!/usr/bin/env bash # 把处理好的成片上传到相册服务器。 # # 设计原则:先在服务器暂存区校验通过,才动相册目录;同名文件默认不覆盖。 # 用法: # bash tools/upload_to_gallery.sh # 传 photos/polaroid 到 /home/gallery/photos # bash tools/upload_to_gallery.sh --dry-run # 只校验本地清单并打印计划,不联网 # bash tools/upload_to_gallery.sh --dest /home/gallery/photos/polaroid # 传到子目录,绝不覆盖原图 # bash tools/upload_to_gallery.sh --overwrite # 明确允许覆盖同名文件 # # 上传完还需要在相册后台 Settings -> Scan All 触发扫描才会出现在界面里。 set -euo pipefail SRC="photos/polaroid" HOST="tx-ubuntu" DEST="/home/gallery/photos" MANIFEST="_sha256.txt" OVERWRITE=0 DRY_RUN=0 OWNER="gallery:gallery" while [ $# -gt 0 ]; do case "$1" in --src) SRC="$2"; shift 2 ;; --host) HOST="$2"; shift 2 ;; --dest) DEST="$2"; shift 2 ;; --owner) OWNER="$2"; shift 2 ;; --overwrite) OVERWRITE=1; shift ;; --dry-run) DRY_RUN=1; shift ;; -h|--help) sed -n '2,15p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; *) echo "未知参数:$1" >&2; exit 2 ;; esac done cd "$SRC" count=$(ls -1 *.jpg 2>/dev/null | wc -l | tr -d ' ') [ "$count" -gt 0 ] || { echo "在 $SRC 里没找到 .jpg" >&2; exit 1; } echo "== 1/6 本地校验($count 个文件)==" if [ -f "$MANIFEST" ]; then sha256sum -c "$MANIFEST" else echo "没有 $MANIFEST,现场生成一份" sha256sum *.jpg | tee "$MANIFEST" fi echo echo "== 2/6 计划 ==" echo " 源目录 $(pwd)" echo " 目标 $HOST:$DEST" echo " 同名处理 $( [ "$OVERWRITE" = 1 ] && echo '允许覆盖' || echo '不覆盖(发现同名就中止)' )" if [ "$DRY_RUN" = 1 ]; then echo echo "(--dry-run,到此为止,没有联网)" exit 0 fi echo "== 3/6 连通性 ==" if ! ssh -o BatchMode=yes -o ConnectTimeout=8 "$HOST" true 2>/dev/null; then { echo "连不上 $HOST 的 SSH。依次排查:" echo " 1) 当前网络是否封了出站 22 端口(酒店/办公网/部分运营商常见)。验证:" echo " ssh -o ConnectTimeout=8 git@github.com # 也超时就说明是本机网络封了 22" echo " 换手机热点或别的网络再跑。" echo " 2) ~/.ssh/config 里有没有这个别名,或用 --host user@ip。" echo " 3) 服务器是否在线:curl -sI https://gallery.fukun.net | head -1" } >&2 exit 4 fi stage="/tmp/pv-photos-$(date +%Y%m%d-%H%M%S)" echo echo "== 4/6 上传到暂存区 $stage ==" ssh "$HOST" "mkdir -p '$stage'" if command -v rsync >/dev/null 2>&1; then rsync -av --partial ./*.jpg "$MANIFEST" "$HOST:$stage/" else scp ./*.jpg "$MANIFEST" "$HOST:$stage/" fi echo echo "== 5/6 服务器侧校验 ==" ssh "$HOST" "cd '$stage' && sha256sum -c '$MANIFEST'" echo echo "== 6/6 入库 ==" clash=$(ssh "$HOST" "cd '$stage' && for f in *.jpg; do [ -e '$DEST'/\$f ] && echo \"\$f\"; done" || true) if [ -n "$clash" ]; then echo "以下文件在 $DEST 里已存在:" echo "$clash" | sed 's/^/ /' if [ "$OVERWRITE" != 1 ]; then echo echo "已中止,服务器暂存区保留在 $stage(可以改 --dest 传到子目录重来)。" >&2 echo "确认要覆盖原图时加 --overwrite。" >&2 exit 3 fi echo "(--overwrite 已开启,继续覆盖)" fi # 顺序很讲究,三步都不能换: # 1) 先改暂存区(真实目录)属主。不能对 $DEST 做 chown -R:/home/gallery/photos 是指向 # /data/gallery-photos 的软链接,chown -R 默认不跟随,会「退出码 0 但什么都没做」。 # 2) 再把文件 mtime 设成 EXIF 拍摄时间。Photoview 建 media 记录时用 mtime 初始化 # date_shot,而它的 EXIF 任务有 bug(先存库后赋值 + 已关联即提前返回)不会纠正, # 所以 mtime 就是相册里最终显示和排序的日期。 # 3) 最后用 root 的 mv 移入相册(保留属主和 mtime)。 ssh "$HOST" "sudo chown -R '$OWNER' '$stage'" ssh "$HOST" "for f in '$stage'/*.jpg; do sudo -u '${OWNER%%:*}' exiftool -q '-FileModifyDate/dev/null || true; done" echo "暂存区文件的 mtime(应等于拍摄时间):" ssh "$HOST" "ls -l --time-style=+%Y-%m-%d_%H:%M '$stage'/*.jpg | awk '{print \" \" \$6, \$7}' | head -5" ssh "$HOST" "sudo mv '$stage'/*.jpg '$DEST'/" owners=$(ssh "$HOST" "ls -l '$DEST'/*.jpg | awk '{print \$3\":\"\$4}' | sort | uniq -c") echo "目标目录属主统计(应为 $OWNER):" echo "$owners" | sed 's/^/ /' if echo "$owners" | grep -qv "$OWNER"; then echo "注意:上面有不属于 $OWNER 的文件,检查一下(可能是旧文件或权限漂移)。" >&2 fi ssh "$HOST" "rm -rf '$stage'" echo echo "完成。还差一步:相册后台 Settings -> Scan All 触发扫描,才会出现在界面里。" echo "公开原图地址形如 https://cdn.fukun.net/gallery/photos/<文件名>"