From cf8d9919742bf164769080a0f8ed6148a177f079 Mon Sep 17 00:00:00 2001 From: Odboy Date: Sun, 25 May 2025 21:01:16 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20EXIF=E8=A7=A3=E6=9E=90=E8=B6=8A=E7=95=8C?= =?UTF-8?q?=E4=B8=8E=E8=BD=AC=E6=AD=A3=E4=B8=A2=E6=96=87=E4=BB=B6=E3=80=81?= =?UTF-8?q?.psb=E5=BD=92=E7=B1=BB=E3=80=81=E6=97=A5=E5=BF=97=E6=8E=A5?= =?UTF-8?q?=E7=AE=A1panic=E6=81=A2=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/organize/organize.go | 4 ++-- internal/probe/exif.go | 20 ++++++++++++++------ internal/service/scan.go | 13 +++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/internal/organize/organize.go b/internal/organize/organize.go index cf3fab7..6188679 100644 --- a/internal/organize/organize.go +++ b/internal/organize/organize.go @@ -280,8 +280,8 @@ type imageProbeResult struct { // 只读数据库(查指纹),这是安全的。 func (o *Organizer) probeImage(f scanner.File) imageProbeResult { r := imageProbeResult{} - // PSD 不体检,直接标记"原样收走" - if strings.EqualFold(f.Ext, ".psd") { + // PSD/PSB 设计稿不体检,直接标记"原样收走" + if strings.EqualFold(f.Ext, ".psd") || strings.EqualFold(f.Ext, ".psb") { r.psd = true return r } diff --git a/internal/probe/exif.go b/internal/probe/exif.go index 4083a3c..0eb0ebf 100644 --- a/internal/probe/exif.go +++ b/internal/probe/exif.go @@ -71,13 +71,21 @@ func RotateImage(ffmpegPath, filePath string) error { } } - // 用转正后的文件替换原文件 - if err := os.Remove(filePath); err != nil { + // 用转正后的文件替换原文件:先把原文件改名成备份,再让转正结果顶上,最后删备份。 + // 这样即使中途失败,原文件也还在(只是改了名),不会弄丢。 + backup := filePath + ".rotate.bak" + if err := os.Rename(filePath, backup); err != nil { + _ = os.Remove(tmp) + return fmt.Errorf("备份原文件失败: %v", err) + } + if err := os.Rename(tmp, filePath); err != nil { + // 转正结果放不回去:把备份挪回来尽量恢复原状 + _ = os.Rename(backup, filePath) _ = os.Remove(tmp) return fmt.Errorf("替换原文件失败: %v", err) } - if err := os.Rename(tmp, filePath); err != nil { - return fmt.Errorf("替换原文件失败: %v", err) + if err := os.Remove(backup); err != nil { + fmt.Printf("=== 删除原文件备份失败(可手动清理 %s): %v\n", backup, err) } return nil } @@ -120,7 +128,7 @@ func readExifAPP1(filePath string) ([]byte, error) { if segLen < 2 { break } - if marker == 0xE1 && segLen >= 8 && string(buf[pos+4:pos+8]) == "Exif" { + if marker == 0xE1 && segLen >= 8 && pos+8 <= len(buf) && string(buf[pos+4:pos+8]) == "Exif" { end := pos + 2 + segLen if end > len(buf) { end = len(buf) @@ -254,7 +262,7 @@ func stripExifAPP1(rest []byte) []byte { out = append(out, rest[pos:]...) return out } - isExif := marker == 0xE1 && segLen >= 8 && string(rest[pos+4:pos+8]) == "Exif" + isExif := marker == 0xE1 && segLen >= 8 && pos+8 <= len(rest) && string(rest[pos+4:pos+8]) == "Exif" if !isExif { out = append(out, rest[pos:pos+2+segLen]...) } diff --git a/internal/service/scan.go b/internal/service/scan.go index 467b656..9980395 100644 --- a/internal/service/scan.go +++ b/internal/service/scan.go @@ -17,6 +17,7 @@ import ( "os/exec" "path/filepath" "regexp" + "runtime/debug" "strings" "sync" "time" @@ -327,6 +328,18 @@ func (s *ScanService) run(rootDir, destDir string) { } }() + // 收尾必须恢复 stdout 并关闭管道,否则整个进程后续输出都会丢进这根无人读的管道里。 + // 用 defer 保证即使 runScan 中途 panic 也不会留下这种状态;panic 还会被接住, + // 转成"出错"状态推给前端,而不是让整个程序崩溃。 + defer func() { + os.Stdout = oldStdout + _ = w.Close() + <-done // 等读日志的 goroutine 把剩下的日志读完 + if rcv := recover(); rcv != nil { + s.finish(ScanError, fmt.Sprintf("扫描整理内部错误: %v\n%s", rcv, debug.Stack())) + } + }() + // 真正开始干活。期间程序里所有 fmt.Println/Printf 都会进上面的管道 scanErr := s.runScan(rootDir, destDir)