fix: EXIF解析越界与转正丢文件、.psb归类、日志接管panic恢复
This commit is contained in:
@@ -280,8 +280,8 @@ type imageProbeResult struct {
|
|||||||
// 只读数据库(查指纹),这是安全的。
|
// 只读数据库(查指纹),这是安全的。
|
||||||
func (o *Organizer) probeImage(f scanner.File) imageProbeResult {
|
func (o *Organizer) probeImage(f scanner.File) imageProbeResult {
|
||||||
r := imageProbeResult{}
|
r := imageProbeResult{}
|
||||||
// PSD 不体检,直接标记"原样收走"
|
// PSD/PSB 设计稿不体检,直接标记"原样收走"
|
||||||
if strings.EqualFold(f.Ext, ".psd") {
|
if strings.EqualFold(f.Ext, ".psd") || strings.EqualFold(f.Ext, ".psb") {
|
||||||
r.psd = true
|
r.psd = true
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-6
@@ -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)
|
_ = os.Remove(tmp)
|
||||||
return fmt.Errorf("替换原文件失败: %v", err)
|
return fmt.Errorf("替换原文件失败: %v", err)
|
||||||
}
|
}
|
||||||
if err := os.Rename(tmp, filePath); err != nil {
|
if err := os.Remove(backup); err != nil {
|
||||||
return fmt.Errorf("替换原文件失败: %v", err)
|
fmt.Printf("=== 删除原文件备份失败(可手动清理 %s): %v\n", backup, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -120,7 +128,7 @@ func readExifAPP1(filePath string) ([]byte, error) {
|
|||||||
if segLen < 2 {
|
if segLen < 2 {
|
||||||
break
|
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
|
end := pos + 2 + segLen
|
||||||
if end > len(buf) {
|
if end > len(buf) {
|
||||||
end = len(buf)
|
end = len(buf)
|
||||||
@@ -254,7 +262,7 @@ func stripExifAPP1(rest []byte) []byte {
|
|||||||
out = append(out, rest[pos:]...)
|
out = append(out, rest[pos:]...)
|
||||||
return out
|
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 {
|
if !isExif {
|
||||||
out = append(out, rest[pos:pos+2+segLen]...)
|
out = append(out, rest[pos:pos+2+segLen]...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"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 都会进上面的管道
|
// 真正开始干活。期间程序里所有 fmt.Println/Printf 都会进上面的管道
|
||||||
scanErr := s.runScan(rootDir, destDir)
|
scanErr := s.runScan(rootDir, destDir)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user