/* * GodoOS - A lightweight cloud desktop * Copyright (C) 2024 https://godoos.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ package files import ( "compress/bzip2" "compress/gzip" "fmt" "godo/libs" "io" "os" "path/filepath" "strings" "archive/tar" "archive/zip" ) // Decompress 解压文件到指定目录,支持多种压缩格式 func Decompress(compressedFilePath, destPath string) (string, error) { ext := filepath.Ext(compressedFilePath) switch ext { case ".zip": return Unzip(compressedFilePath, destPath) case ".tar": tarFile, err := os.Open(compressedFilePath) if err != nil { return "", err } defer tarFile.Close() return Untar(tarFile, destPath) case ".gz": return Untargz(compressedFilePath, destPath) case ".bz2": return Untarbz2(compressedFilePath, destPath) default: return "", fmt.Errorf("unsupported compression format: %s", ext) } } // Untar 解压.tar文件到指定目录 func Untar(reader io.Reader, destPath string) (string, error) { var topLevelDirName string tarReader := tar.NewReader(reader) for { header, err := tarReader.Next() if err == io.EOF { break } if err != nil { return "", err } // Determine the top-level directory name if header.Typeflag == tar.TypeDir && topLevelDirName == "" { topLevelDirName = header.Name } else { topLevelDirName = destPath } target := filepath.Join(destPath, header.Name) if header.Typeflag == tar.TypeDir { if err := os.MkdirAll(target, os.FileMode(header.Mode)); err != nil { return "", err } continue } if err := os.MkdirAll(filepath.Dir(target), os.ModePerm); err != nil { return "", err } outFile, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR|os.O_TRUNC, os.FileMode(header.Mode)) if err != nil { return "", err } defer outFile.Close() if _, err := io.Copy(outFile, tarReader); err != nil { return "", err } } // Return the full path of the top-level directory topLevelDestPath := filepath.Join(destPath, topLevelDirName) return topLevelDestPath, nil } // Untargz 解压.tar.gz文件到指定目录 func Untargz(targzFilePath, destPath string) (string, error) { gzFile, err := os.Open(targzFilePath) if err != nil { return "", err } defer gzFile.Close() gzReader, err := gzip.NewReader(gzFile) if err != nil { return "", err } defer gzReader.Close() return Untar(gzReader, destPath) } // Untarbz2 解压.tar.bz2文件到指定目录 func Untarbz2(tarbz2FilePath, destPath string) (string, error) { bz2File, err := os.Open(tarbz2FilePath) if err != nil { return "", err } defer bz2File.Close() bz2Reader := bzip2.NewReader(bz2File) return Untar(bz2Reader, destPath) } // DecompressZip 解压zip文件到指定目录 func Unzip(zipFilePath, destPath string) (string, error) { r, err := zip.OpenReader(zipFilePath) if err != nil { return "", err } defer r.Close() // 初始化顶层目录名称为空 var topLevelDirName string var topLevelDestPath string // 遍历ZIP文件中的每个条目 for _, f := range r.File { // 如果是目录且不是根目录,则更新顶层目录名称 if f.FileInfo().IsDir() && f.Name != "" { // 去掉末尾的斜杠 cleanedName := strings.TrimSuffix(f.Name, string(os.PathSeparator)) if topLevelDirName == "" || cleanedName < topLevelDirName { topLevelDirName = cleanedName } } } // 检查是否找到了顶层目录 if topLevelDirName == "" { topLevelDestPath = destPath } else { // 完整的顶层目录路径 topLevelDestPath = filepath.Join(destPath, topLevelDirName) } // 再次遍历ZIP文件中的每个条目,这次进行解压操作 for _, f := range r.File { rc, err := f.Open() if err != nil { return "", err } defer rc.Close() // 调整路径以确保所有文件都在顶层目录内 //path := filepath.Join(topLevelDestPath, strings.TrimPrefix(f.Name, topLevelDirName+string(os.PathSeparator))) path := filepath.Join(topLevelDestPath, strings.TrimPrefix(f.Name, topLevelDirName)) // 确保父目录存在 if !libs.PathExists(filepath.Dir(path)) { if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { return "", err } } if f.FileInfo().IsDir() { // 创建目录 if !libs.PathExists(path) { if err := os.Mkdir(path, f.Mode()); err != nil { return "", err } } } else { // 创建并写入文件 outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return "", err } defer outFile.Close() if _, err := io.Copy(outFile, rc); err != nil { return "", err } } } // 返回完整的顶层目录路径 return topLevelDestPath, nil } // IsSupportedCompressionFormat 判断文件后缀是否为支持的压缩格式 func IsSupportedCompressionFormat(filename string) bool { supportedFormats := map[string]bool{ ".zip": true, ".tar": true, ".gz": true, ".bz2": true, } for format := range supportedFormats { if strings.HasSuffix(strings.ToLower(filename), format) { return true } } return false } // HandlerFile 根据文件后缀判断是否支持解压,支持则解压,否则移动文件 func HandlerFile(filePath string, destDir string) (string, error) { // log.Printf("HandlerFile: %s", filePath) // log.Printf("destDir: %s", destDir) if IsSupportedCompressionFormat(filePath) { return Decompress(filePath, destDir) } else { return "", fmt.Errorf("unsupported compression format: %s", filePath) } }