- 添加跳过ffprobe相关文件的逻辑,避免处理临时释放文件 - 修复图片处理中的变量引用错误 - 实现大文件指纹复用机制,避免重复全量读取计算MD5 - 新增文件头尾采样指纹算法,提升大文件处理性能 - 重构ffprobe内嵌资源管理,支持多平台自动选择对应版本 - 添加媒体信息数据库的指纹索引和去向跟踪功能 - 优化程序启动流程,失败时跳过媒体整理但保留图片处理 - 更新文档说明多平台ffprobe支持和构建要求
163 lines
5.1 KiB
Go
163 lines
5.1 KiB
Go
package util
|
|
|
|
import (
|
|
"crypto/md5"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"github.com/gabriel-vasile/mimetype"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// CreateDir 创建目录(含递归创建父目录)
|
|
func CreateDir(dirPath string) bool {
|
|
err := os.MkdirAll(dirPath, 0755)
|
|
if err != nil {
|
|
fmt.Printf("=== 创建目录失败: %s, 错误: %v\n", dirPath, err)
|
|
}
|
|
return err == nil
|
|
}
|
|
|
|
// GetFileDirectory 获取文件所在文件夹
|
|
func GetFileDirectory(filePath string) string {
|
|
directoryPath, _ := filepath.Split(filePath)
|
|
return directoryPath
|
|
}
|
|
|
|
// ReadFileMimeInfo 获取文件mime信息,失败时返回nil
|
|
func ReadFileMimeInfo(filepath string) *mimetype.MIME {
|
|
mt, err := mimetype.DetectFile(filepath)
|
|
if err != nil {
|
|
fmt.Printf("=== 读取Mime信息失败: %s, 错误: %v\n", filepath, err)
|
|
return nil
|
|
}
|
|
return mt
|
|
}
|
|
|
|
// CheckFileIsExist 判断文件是否存在,存在返回 true,不存在返回false
|
|
func CheckFileIsExist(filename string) bool {
|
|
var exist = true
|
|
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
|
exist = false
|
|
}
|
|
return exist
|
|
}
|
|
|
|
// WriteByteArraysToFile 写字节数组到文件
|
|
func WriteByteArraysToFile(content []byte, filename string) error {
|
|
return ioutil.WriteFile(filename, content, 0777)
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// MD5指纹加速相关常量
|
|
const (
|
|
FingerprintThreshold = 1 << 20 // 文件大于1MB才使用指纹复用,避免全量MD5
|
|
fingerprintHeadSize = 256 << 10 // 文件头采样256KB
|
|
fingerprintTailSize = 256 << 10 // 文件尾采样256KB
|
|
)
|
|
|
|
// CalcFileFingerprint 计算文件的头部与尾部采样MD5(只读取很少字节,用于快速识别相同文件)
|
|
func CalcFileFingerprint(filePath string, size int64) (headMd5 string, 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
|
|
}
|
|
|
|
//// 移动文件到根目录
|
|
//func renameFile(rootDir string, modelType string, videoList []string, pathSeparator string) {
|
|
// total := len(videoList)
|
|
// var count = 0
|
|
// bar := goPrint.NewBar(100)
|
|
// bar.SetNotice("=== 重命名文件:")
|
|
// bar.SetGraph(">")
|
|
// for _, videoFilePath := range videoList {
|
|
// wh := videoPath2WidthHeightTagMap[videoFilePath]
|
|
// fileName := vars.GlobalFilePath2FileNameMap[videoFilePath]
|
|
// if strings.Contains(fileName, videoTag) { // 处理过了
|
|
// fileNames := strings.Split(fileName, videoTag)
|
|
// if len(fileNames) == 2 {
|
|
// fileName = fileNames[1]
|
|
// targetFilePath := rootDir + pathSeparator + "[" + videoPath2DurationMap[videoFilePath] + "]" + modelType + wh + videoTag + fileName
|
|
// err := os.Rename(videoFilePath, targetFilePath)
|
|
// if err != nil {
|
|
// fmt.Printf("=== 重命名异常: %s \n", videoFilePath)
|
|
// }
|
|
// }
|
|
// } else {
|
|
// targetFilePath := rootDir + pathSeparator + "[" + videoPath2DurationMap[videoFilePath] + "]" + modelType + wh + videoTag + " - " + fileName
|
|
// err := os.Rename(videoFilePath, targetFilePath)
|
|
// if err != nil {
|
|
// fmt.Printf("=== 重命名异常: %s \n", videoFilePath)
|
|
// }
|
|
// }
|
|
// count = count + 1
|
|
// bar.PrintBar(util.CalcPercentage(count, total))
|
|
// }
|
|
// bar.PrintEnd("=== Finish")
|
|
//}
|
|
//
|
|
//// 移动文件到原目录
|
|
//func renameFileV2(modelType string, videoList []string) {
|
|
// total := len(videoList)
|
|
// var count = 0
|
|
// bar := goPrint.NewBar(100)
|
|
// bar.SetNotice("=== 重命名文件:")
|
|
// bar.SetGraph(">")
|
|
// for _, videoFilePath := range videoList {
|
|
// wh := videoPath2WidthHeightTagMap[videoFilePath]
|
|
// fileName := vars.GlobalFilePath2FileNameMap[videoFilePath]
|
|
// filePath := util.GetFileDirectory(videoFilePath)
|
|
// if strings.Contains(fileName, videoTag) { // 处理过了
|
|
// fileNames := strings.Split(fileName, videoTag)
|
|
// if len(fileNames) == 2 {
|
|
// fileName = fileNames[1]
|
|
// targetFilePath := filePath + "[" + videoPath2DurationMap[videoFilePath] + "]" + modelType + wh + videoTag + fileName
|
|
// err := os.Rename(videoFilePath, targetFilePath)
|
|
// if err != nil {
|
|
// fmt.Printf("=== 重命名异常: %s \n", videoFilePath)
|
|
// }
|
|
// }
|
|
// } else {
|
|
// targetFilePath := filePath + "[" + videoPath2DurationMap[videoFilePath] + "]" + modelType + wh + videoTag + " - " + fileName
|
|
// err := os.Rename(videoFilePath, targetFilePath)
|
|
// if err != nil {
|
|
// fmt.Printf("=== 重命名异常: %s \n", videoFilePath)
|
|
// }
|
|
// }
|
|
// count = count + 1
|
|
// bar.PrintBar(util.CalcPercentage(count, total))
|
|
// }
|
|
// bar.PrintEnd("=== Finish")
|
|
//}
|