- 删除 vars 全局状态,按职责拆分 internal/ 包:scanner/probe/classify/organize/mover/store/digest - 数据驱动归类规则替代图片/视频重复分支,store 改为结构体实例 - build.sh 按系统类型编译:Windows 用 upx 压缩,macOS/Linux 不压缩 - 忽略目录拆分:图片_已忽略 / 视频_已忽略 - 输出构建产物加入 .gitignore
54 lines
1.4 KiB
Go
54 lines
1.4 KiB
Go
// Package digest 提供文件 MD5 与指纹采样计算,供媒体入库时复用。
|
|
package digest
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
const (
|
|
// FingerprintThreshold 文件大于该字节数才使用指纹复用,避免全量MD5
|
|
FingerprintThreshold = 1 << 20
|
|
fingerprintHeadSize = 256 << 10 // 文件头采样256KB
|
|
fingerprintTailSize = 256 << 10 // 文件尾采样256KB
|
|
)
|
|
|
|
// CalcFileMd5 计算文件MD5(分块读取,适合大文件)
|
|
func CalcFileMd5(filePath string) (string, error) {
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer file.Close()
|
|
h := md5.New()
|
|
if _, err := io.Copy(h, file); err != nil {
|
|
return "", err
|
|
}
|
|
return hex.EncodeToString(h.Sum(nil)), nil
|
|
}
|
|
|
|
// CalcFileFingerprint 计算文件头部与尾部采样MD5(只读取很少字节,用于快速识别相同文件)
|
|
func CalcFileFingerprint(filePath string, size int64) (headMd5, tailMd5 string, err error) {
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
defer file.Close()
|
|
h := md5.New()
|
|
if _, err := io.CopyN(h, file, fingerprintHeadSize); err != nil {
|
|
return "", "", err
|
|
}
|
|
headMd5 = hex.EncodeToString(h.Sum(nil))
|
|
h = md5.New()
|
|
if _, err := file.Seek(size-fingerprintTailSize, io.SeekStart); err != nil {
|
|
return "", "", err
|
|
}
|
|
if _, err := io.CopyN(h, file, fingerprintTailSize); err != nil {
|
|
return "", "", err
|
|
}
|
|
tailMd5 = hex.EncodeToString(h.Sum(nil))
|
|
return headMd5, tailMd5, nil
|
|
}
|