U:调整依赖自动寻找路径

This commit is contained in:
jimmy committed 2025-08-02 18:03:09 +08:00
1 parent 137efc201a
commit d7814edbd1
2 files changed
+181 -15

No files matched your search

+45 -6
View File
@@ -123,12 +123,12 @@ func detectTools(baseDir *string) error {
// 检测 libvips
if VipsPath == "" {
VipsPath = findTool("vips", execDir, "vips")
VipsPath = findTool("vips", execDir, "libvips")
} else {
// 验证配置的路径是否有效
if !validateToolPath(VipsPath) {
logger.Warn("配置的LibVips路径无效,尝试自动检测", zap.String("path", VipsPath))
VipsPath = findTool("vips", execDir, "vips")
VipsPath = findTool("vips", execDir, "libvips")
}
}
if VipsPath == "" {
@@ -170,21 +170,34 @@ func findTool(name string, execDir string, identification string) string {
// 2. 执行文件同目录的 bin 子目录
// 3. 执行文件同目录的 tools 子目录
// 4. 系统 PATH
searchPaths := []string{
filepath.Join(execDir, exeName),
filepath.Join(execDir, "bin", exeName),
filepath.Join(execDir, "tools", exeName),
filepath.Join(execDir, "tools", name, exeName),
filepath.Join(execDir, "tools", name, "bin", exeName),
filepath.Join(execDir, "tools", identification, exeName),
filepath.Join(execDir, "tools", identification, name, exeName),
filepath.Join(execDir, "tools", identification, name, "bin", exeName),
filepath.Join(execDir, "tools", "bin", name, exeName),
filepath.Join(execDir, "tools", "bin", exeName),
filepath.Join(execDir, identification, exeName),
filepath.Join(execDir, identification, "bin", exeName),
filepath.Join(execDir, identification, "tools", exeName),
filepath.Join(execDir, identification, "tools", name, exeName),
filepath.Join(execDir, identification, "tools", name, "bin", exeName),
filepath.Join(execDir, identification, "tools", "bin", name, exeName),
filepath.Join(execDir, identification, "tools", "bin", exeName),
filepath.Join(execDir, identification, "tools", identification, exeName),
filepath.Join(execDir, identification, "tools", identification, name, exeName),
filepath.Join(execDir, identification, "tools", identification, name, "bin", exeName),
filepath.Join(execDir, identification, "tools", identification, "bin", name, exeName),
filepath.Join(execDir, identification, "tools", identification, "bin", exeName),
filepath.Join(execDir, "tools", identification, exeName),
filepath.Join(execDir, "tools", identification, name, exeName),
filepath.Join(execDir, "tools", identification, name, "bin", exeName),
filepath.Join(execDir, "tools", identification, "bin", name, exeName),
filepath.Join(execDir, "tools", identification, "bin", exeName),
}
jsonBytes, _ := json.Marshal(searchPaths)
msg := string(jsonBytes)
@@ -207,6 +220,32 @@ func findTool(name string, execDir string, identification string) string {
return ""
}
// generateSearchPaths 返回 execDir + 任意 segment 组合 + exeName 的路径列表
func generateSearchPaths(execDir, exeName string, segments ...string) []string {
var result []string
// 加入 execDir/exeName 本身
result = append(result, filepath.Join(execDir, exeName))
n := len(segments)
// 枚举所有非空子集(使用位图方法)
for i := 1; i < (1 << n); i++ {
var parts []string
for j := 0; j < n; j++ {
if (i>>j)&1 == 1 {
parts = append(parts, segments[j])
}
}
// 拼接 execDir + parts... + exeName
full := append([]string{execDir}, parts...)
full = append(full, exeName)
result = append(result, filepath.Join(full...))
}
return result
}
// EnsureInitialized 确保工具已初始化
func EnsureInitialized(config *Config, baseDir *string) error {
if toolsInitErr == nil && ImageMagickPath == "" && ExifToolPath == "" && VipsPath == "" {
+136 -9
View File
@@ -22,6 +22,7 @@ import (
"rear/internal/utils"
"rear/pkg/logger"
utilsPkg "rear/pkg/utils"
"runtime"
"strings"
"syscall"
"time"
@@ -289,12 +290,49 @@ func initializeToolDependencies() error {
return nil
}
// getPlatformDir 获取当前平台对应的目录名
func getPlatformDir() string {
osName := runtime.GOOS
arch := runtime.GOARCH
switch osName {
case "windows":
if arch == "amd64" {
return "windows_amd64"
}
return "windows_" + arch
case "darwin":
if arch == "amd64" {
return "darwin_amd64"
} else if arch == "arm64" {
return "darwin_arm64"
}
return "darwin_" + arch
case "linux":
if arch == "amd64" {
return "linux_amd64"
}
return "linux_" + arch
default:
return osName + "_" + arch
}
}
// extractToolFromZip 从压缩包中解压工具到构建目录
func extractToolFromZip(toolName string) error {
logger.Infof("处理工具: %s", toolName)
// 源压缩包目录
sourceDir := filepath.Join("tools", toolName)
// 获取当前平台目录
platformDir := getPlatformDir()
logger.Infof("当前平台: %s", platformDir)
// 源压缩包目录(包含平台子目录)
sourceDir := filepath.Join("tools", platformDir, toolName)
// 检查平台特定目录是否存在
if _, err := os.Stat(sourceDir); os.IsNotExist(err) {
return fmt.Errorf("平台目录不存在: %s,请确保为当前平台 %s 提供了 %s 工具包", sourceDir, platformDir, toolName)
}
// 目标解压目录
targetDir := filepath.Join(config.CONFIG.AppDir, "tools", toolName)
@@ -318,10 +356,57 @@ func extractToolFromZip(toolName string) error {
return fmt.Errorf("解压失败: %v", err)
}
// Windows平台特定的后处理
if err := postProcessTool(toolName, targetDir); err != nil {
logger.Warnf("工具 %s 后处理失败: %v", toolName, err)
}
logger.Infof("工具 %s 解压完成", toolName)
return nil
}
// postProcessTool 对解压后的工具进行平台特定的后处理
func postProcessTool(toolName, targetDir string) error {
// 只在Windows平台进行后处理
if runtime.GOOS != "windows" {
return nil
}
switch toolName {
case "exiftool":
return postProcessExiftool(targetDir)
}
return nil
}
// postProcessExiftool 对exiftool进行Windows平台的后处理
func postProcessExiftool(targetDir string) error {
// 检查是否存在 exiftool(-k).exe
oldPath := filepath.Join(targetDir, "exiftool(-k).exe")
newPath := filepath.Join(targetDir, "exiftool.exe")
// 如果 exiftool(-k).exe 存在
if fileExists(oldPath) {
// 如果 exiftool.exe 已经存在,先删除它
if fileExists(newPath) {
if err := os.Remove(newPath); err != nil {
return fmt.Errorf("删除现有的 exiftool.exe 失败: %v", err)
}
logger.Infof("删除现有的 exiftool.exe")
}
// 重命名 exiftool(-k).exe 为 exiftool.exe
if err := os.Rename(oldPath, newPath); err != nil {
return fmt.Errorf("重命名 exiftool(-k).exe 为 exiftool.exe 失败: %v", err)
}
logger.Infof("已将 exiftool(-k).exe 重命名为 exiftool.exe")
}
return nil
}
// selectZipFile 选择合适的压缩包文件
func selectZipFile(sourceDir, toolName string) (string, error) {
// 扫描目录中的zip文件
@@ -367,6 +452,11 @@ func isToolReady(targetDir, toolName string) bool {
// 根据不同工具检查关键文件是否存在
switch toolName {
case "exiftool":
// Windows平台下经过后处理后应该只有 exiftool.exe
if runtime.GOOS == "windows" {
return fileExists(filepath.Join(targetDir, "exiftool.exe"))
}
// 其他平台检查两个可能的文件名
return fileExists(filepath.Join(targetDir, "exiftool(-k).exe")) ||
fileExists(filepath.Join(targetDir, "exiftool.exe"))
case "imagemagick":
@@ -415,23 +505,60 @@ func extractZip(src, dest string) error {
return nil
}
// extractFile 解压单个文件
// extractFile 解压单个文件(扁平化解压,跳过顶层目录)
func extractFile(f *zip.File, destDir string) error {
// 获取文件在压缩包中的路径
zipPath := f.Name
// 如果是以 / 或 \ 开头的路径,去掉开头的分隔符
zipPath = strings.TrimPrefix(zipPath, "/")
zipPath = strings.TrimPrefix(zipPath, "\\")
// 将路径分割为目录组件
pathParts := strings.Split(zipPath, "/")
if len(pathParts) == 0 {
return nil // 跳过空路径
}
// 如果只有一个组件且是目录,跳过(这通常是顶层目录)
if len(pathParts) == 1 && f.FileInfo().IsDir() {
return nil
}
// 构建目标路径:跳过第一个目录组件(顶层目录)
var relativePath string
if len(pathParts) > 1 {
// 跳过顶层目录,保留子路径
relativePath = filepath.Join(pathParts[1:]...)
} else {
// 如果只有一个组件且是文件,直接使用文件名
relativePath = pathParts[0]
}
// 构建完整的目标路径
path := filepath.Join(destDir, f.Name)
targetPath := filepath.Join(destDir, relativePath)
// 确保路径安全(防止zip slip攻击)
if !strings.HasPrefix(path, filepath.Clean(destDir)+string(os.PathSeparator)) {
cleanDestDir := filepath.Clean(destDir)
cleanTargetPath := filepath.Clean(targetPath)
// 检查目标路径是否在destDir范围内
if !strings.HasPrefix(cleanTargetPath, cleanDestDir) ||
(len(cleanTargetPath) > len(cleanDestDir) &&
cleanTargetPath[len(cleanDestDir)] != filepath.Separator) {
return fmt.Errorf("无效的文件路径: %s", f.Name)
}
if f.FileInfo().IsDir() {
// 创建目录
return os.MkdirAll(path, f.FileInfo().Mode())
// 创建目录(如果需要的话)
if len(pathParts) > 1 {
return os.MkdirAll(targetPath, f.FileInfo().Mode())
}
return nil // 跳过顶层目录
}
// 创建父目录
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
if err := os.MkdirAll(filepath.Dir(targetPath), 0755); err != nil {
return err
}
@@ -443,7 +570,7 @@ func extractFile(f *zip.File, destDir string) error {
defer rc.Close()
// 创建目标文件
outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.FileInfo().Mode())
outFile, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.FileInfo().Mode())
if err != nil {
return err
}