Добавлена возможность асинхронного создания снапшотов
А также обновлены зависимости
This commit is contained in:
@@ -95,6 +95,104 @@ func CreateArchive(sourceDir, targetPath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateArchiveWithProgress creates a ZIP archive with progress reporting.
|
||||
// onProgress is called with the current number of bytes written and the total size.
|
||||
func CreateArchiveWithProgress(sourceDir, targetPath string, onProgress func(current, total int64)) error {
|
||||
info, err := os.Stat(sourceDir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to stat source directory %s: %w", sourceDir, err)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return fmt.Errorf("source %s is not a directory", sourceDir)
|
||||
}
|
||||
|
||||
// Calculate total size
|
||||
var totalSize int64
|
||||
err = filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
totalSize += info.Size()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to calculate total size of %s: %w", sourceDir, err)
|
||||
}
|
||||
|
||||
// Create file for ZIP archive
|
||||
outFile, err := os.Create(targetPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create target archive file %s: %w", targetPath, err)
|
||||
}
|
||||
defer outFile.Close()
|
||||
|
||||
// Create zip.Writer
|
||||
zipWriter := zip.NewWriter(outFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
var currentSize int64
|
||||
|
||||
// Recursively walk sourceDir
|
||||
err = filepath.Walk(sourceDir, func(filePath string, fileInfo os.FileInfo, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return fmt.Errorf("error walking path %s: %w", filePath, walkErr)
|
||||
}
|
||||
|
||||
// Skip sourceDir itself
|
||||
if filePath == sourceDir {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create relative path
|
||||
relativePath := strings.TrimPrefix(filePath, sourceDir+string(filepath.Separator))
|
||||
relativePath = filepath.ToSlash(relativePath)
|
||||
|
||||
// Check if directory
|
||||
if fileInfo.IsDir() {
|
||||
_, err = zipWriter.Create(relativePath + "/")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create directory entry %s in archive: %w", relativePath, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Open file for reading
|
||||
fileToArchive, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file %s for archiving: %w", filePath, err)
|
||||
}
|
||||
defer fileToArchive.Close()
|
||||
|
||||
// Create archive entry
|
||||
zipEntryWriter, err := zipWriter.Create(relativePath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create entry %s in archive: %w", relativePath, err)
|
||||
}
|
||||
|
||||
// Copy content
|
||||
n, err := io.Copy(zipEntryWriter, fileToArchive)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to copy file content %s to archive: %w", filePath, err)
|
||||
}
|
||||
|
||||
currentSize += n
|
||||
if onProgress != nil {
|
||||
onProgress(currentSize, totalSize)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
os.Remove(targetPath)
|
||||
return fmt.Errorf("failed during directory walk for archiving %s: %w", sourceDir, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListArchiveContents читает ZIP-архив и возвращает информацию о его содержимом.
|
||||
func ListArchiveContents(archivePath string) ([]ArchiveEntryInfo, error) {
|
||||
// Открываем ZIP-архив
|
||||
|
||||
Reference in New Issue
Block a user