281 lines
5.3 KiB
Go
281 lines
5.3 KiB
Go
package archiver
|
|
|
|
import (
|
|
"archive/tar"
|
|
"compress/gzip"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"zipprine/internal/models"
|
|
"zipprine/pkg/fileutil"
|
|
)
|
|
|
|
func createTar(config *models.CompressConfig) error {
|
|
outFile, err := os.Create(config.OutputPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outFile.Close()
|
|
|
|
tarWriter := tar.NewWriter(outFile)
|
|
defer tarWriter.Close()
|
|
|
|
return addToTar(tarWriter, config)
|
|
}
|
|
|
|
func createTarGz(config *models.CompressConfig) error {
|
|
outFile, err := os.Create(config.OutputPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outFile.Close()
|
|
|
|
gzWriter, err := gzip.NewWriterLevel(outFile, config.CompressionLevel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer gzWriter.Close()
|
|
|
|
tarWriter := tar.NewWriter(gzWriter)
|
|
defer tarWriter.Close()
|
|
|
|
return addToTar(tarWriter, config)
|
|
}
|
|
|
|
func createGzip(config *models.CompressConfig) error {
|
|
inFile, err := os.Open(config.SourcePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer inFile.Close()
|
|
|
|
outFile, err := os.Create(config.OutputPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outFile.Close()
|
|
|
|
gzWriter, err := gzip.NewWriterLevel(outFile, config.CompressionLevel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer gzWriter.Close()
|
|
|
|
_, err = io.Copy(gzWriter, inFile)
|
|
return err
|
|
}
|
|
|
|
func addToTar(tarWriter *tar.Writer, config *models.CompressConfig) error {
|
|
return filepath.Walk(config.SourcePath, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !fileutil.ShouldInclude(path, config.ExcludePaths, config.IncludePaths) {
|
|
if info.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
relPath, err := filepath.Rel(config.SourcePath, path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
header, err := tar.FileInfoHeader(info, "")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
header.Name = relPath
|
|
|
|
if err := tarWriter.WriteHeader(header); err != nil {
|
|
return err
|
|
}
|
|
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
fmt.Printf(" → %s\n", relPath)
|
|
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
_, err = io.Copy(tarWriter, file)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func extractTar(config *models.ExtractConfig) error {
|
|
file, err := os.Open(config.ArchivePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
tarReader := tar.NewReader(file)
|
|
return extractFromTar(tarReader, config)
|
|
}
|
|
|
|
func extractTarGz(config *models.ExtractConfig) error {
|
|
file, err := os.Open(config.ArchivePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer file.Close()
|
|
|
|
gzReader, err := gzip.NewReader(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer gzReader.Close()
|
|
|
|
tarReader := tar.NewReader(gzReader)
|
|
return extractFromTar(tarReader, config)
|
|
}
|
|
|
|
func extractGzip(config *models.ExtractConfig) error {
|
|
inFile, err := os.Open(config.ArchivePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer inFile.Close()
|
|
|
|
gzReader, err := gzip.NewReader(inFile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer gzReader.Close()
|
|
|
|
outPath := filepath.Join(config.DestPath, filepath.Base(config.ArchivePath))
|
|
outPath = outPath[:len(outPath)-3] // Remove .gz extension
|
|
|
|
outFile, err := os.Create(outPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outFile.Close()
|
|
|
|
_, err = io.Copy(outFile, gzReader)
|
|
return err
|
|
}
|
|
|
|
func extractFromTar(tarReader *tar.Reader, config *models.ExtractConfig) error {
|
|
for {
|
|
header, err := tarReader.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
destPath := filepath.Join(config.DestPath, header.Name)
|
|
|
|
switch header.Typeflag {
|
|
case tar.TypeDir:
|
|
os.MkdirAll(destPath, os.ModePerm)
|
|
case tar.TypeReg:
|
|
if !config.OverwriteAll {
|
|
if _, err := os.Stat(destPath); err == nil {
|
|
fmt.Printf(" ⚠️ Skipping: %s\n", header.Name)
|
|
continue
|
|
}
|
|
}
|
|
|
|
fmt.Printf(" → Extracting: %s\n", header.Name)
|
|
|
|
os.MkdirAll(filepath.Dir(destPath), os.ModePerm)
|
|
|
|
outFile, err := os.Create(destPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if _, err := io.Copy(outFile, tarReader); err != nil {
|
|
outFile.Close()
|
|
return err
|
|
}
|
|
outFile.Close()
|
|
|
|
if config.PreservePerms {
|
|
os.Chmod(destPath, os.FileMode(header.Mode))
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func analyzeTar(path string, isGzipped bool) (*models.ArchiveInfo, error) {
|
|
file, err := os.Open(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
info := &models.ArchiveInfo{
|
|
Type: models.TAR,
|
|
Files: []models.FileInfo{},
|
|
}
|
|
|
|
if isGzipped {
|
|
info.Type = models.TARGZ
|
|
}
|
|
|
|
fileStat, _ := file.Stat()
|
|
info.CompressedSize = fileStat.Size()
|
|
|
|
hash := sha256.New()
|
|
io.Copy(hash, file)
|
|
info.Checksum = fmt.Sprintf("%x", hash.Sum(nil))
|
|
|
|
// Reopen for tar reading
|
|
file.Seek(0, 0)
|
|
|
|
var tarReader *tar.Reader
|
|
if isGzipped {
|
|
gzReader, err := gzip.NewReader(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer gzReader.Close()
|
|
tarReader = tar.NewReader(gzReader)
|
|
} else {
|
|
tarReader = tar.NewReader(file)
|
|
}
|
|
|
|
for {
|
|
header, err := tarReader.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
info.FileCount++
|
|
info.TotalSize += header.Size
|
|
|
|
if len(info.Files) < 100 {
|
|
info.Files = append(info.Files, models.FileInfo{
|
|
Name: header.Name,
|
|
Size: header.Size,
|
|
IsDir: header.Typeflag == tar.TypeDir,
|
|
ModTime: header.ModTime.Format("2006-01-02 15:04:05"),
|
|
})
|
|
}
|
|
}
|
|
|
|
if info.TotalSize > 0 {
|
|
info.CompressionRatio = (1 - float64(info.CompressedSize)/float64(info.TotalSize)) * 100
|
|
}
|
|
|
|
return info, nil
|
|
} |