Skip to content

Commit 26e5bf1

Browse files
committed
refactor: Update version information in resources.rc file
1 parent 2bd7535 commit 26e5bf1

File tree

5 files changed

+269
-139
lines changed

5 files changed

+269
-139
lines changed

compressor/compress.go

+107-54
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import (
55
"fmt"
66
"os"
77
"path"
8-
"sync"
98
"path/filepath"
9+
"strings"
10+
"sync"
11+
"sync/atomic"
1012

11-
"file-compressor/utils"
1213
"file-compressor/encryption"
14+
"file-compressor/utils"
1315
)
1416

1517
// Compress compresses the specified files or folders into a single compressed file.
@@ -26,6 +28,8 @@ func Compress(filenameStrs []string, outputDir *string, password *string) error
2628

2729
// Prepare to store files' content
2830
var files []utils.File
31+
var originalSize int64
32+
var compressedSize int64
2933

3034
// Use a wait group to synchronize goroutines
3135
var wg sync.WaitGroup
@@ -34,40 +38,52 @@ func Compress(filenameStrs []string, outputDir *string, password *string) error
3438
// Channel to receive errors from goroutines
3539
errChan := make(chan error, len(filenameStrs))
3640

37-
// Process each input file or folder
41+
// Function to handle compression of a single file or directory
42+
compressFileOrFolder := func(filePath string) error {
43+
// Check if the file or folder exists
44+
info, err := os.Stat(filePath)
45+
if os.IsNotExist(err) {
46+
return fmt.Errorf("file or folder does not exist: %s", filePath)
47+
}
48+
49+
if info.IsDir() {
50+
utils.ColorPrint(utils.YELLOW, fmt.Sprintf("Compressing folder (%s)\n", filePath))
51+
err := compressFolderRecursive(filePath, &files, &originalSize)
52+
if err != nil {
53+
return err
54+
}
55+
} else {
56+
utils.ColorPrint(utils.YELLOW, fmt.Sprintf("Compressing file (%s)\n", filePath))
57+
// Read file content
58+
content, err := os.ReadFile(filePath)
59+
if err != nil {
60+
return fmt.Errorf("failed to read file %s: %w", filePath, err)
61+
}
62+
63+
// Store file information (name and content)
64+
files = append(files, utils.File{
65+
Name: path.Base(filePath),
66+
Content: content,
67+
})
68+
69+
// Increment original size
70+
atomic.AddInt64(&originalSize, info.Size())
71+
}
72+
return nil
73+
}
74+
75+
// Process each input file or folder recursively
3876
for _, filename := range filenameStrs {
3977
wg.Add(1)
40-
go func(filename string) {
78+
file := filename
79+
go func(filePath string) {
4180
defer wg.Done()
4281

43-
// Check if the file or folder exists
44-
info, err := os.Stat(filename)
45-
if os.IsNotExist(err) {
46-
errChan <- fmt.Errorf("file or folder does not exist: %s", filename)
47-
return
48-
}
49-
50-
// Handle directory recursively
51-
if info.IsDir() {
52-
err := compressFolderRecursive(filename, &files)
53-
if err != nil {
54-
errChan <- err
55-
}
56-
} else {
57-
// Read file content
58-
content, err := os.ReadFile(filename)
59-
if err != nil {
60-
errChan <- fmt.Errorf("failed to read file %s: %w", filename, err)
61-
return
62-
}
63-
64-
// Store file information (name and content)
65-
files = append(files, utils.File{
66-
Name: path.Base(filename),
67-
Content: content,
68-
})
82+
err := compressFileOrFolder(filePath)
83+
if err != nil {
84+
errChan <- err
6985
}
70-
}(filename)
86+
}(file)
7187
}
7288

7389
// Wait for all goroutines to finish
@@ -84,50 +100,58 @@ func Compress(filenameStrs []string, outputDir *string, password *string) error
84100
}
85101

86102
// Compress files using Huffman coding
87-
compressedFile := Zip(files)
103+
compressedFile, err := Zip(files)
104+
if err != nil {
105+
return err
106+
}
88107

89108
// Encrypt compressed content if password is provided
90109
if password != nil && *password != "" {
91-
var err error
92110
compressedFile.Content, err = encryption.Encrypt(compressedFile.Content, *password)
93111
if err != nil {
94112
return fmt.Errorf("encryption error: %w", err)
95113
}
96114
}
97115

98-
//check if the output file and directory exists
116+
// Check if the output directory exists; create if not
99117
if _, err := os.Stat(*outputDir); os.IsNotExist(err) {
100118
err := os.MkdirAll(*outputDir, os.ModePerm)
101119
if err != nil {
102120
return fmt.Errorf("failed to create output directory: %w", err)
103121
}
104122
}
105123

106-
// if the output file already exists, rename it with file(N).bin
107-
if _, err := os.Stat(*outputDir + "/" + compressedFile.Name + ".bin"); err == nil {
108-
count := 1
109-
for {
110-
if _, err := os.Stat(*outputDir + "/" + compressedFile.Name + fmt.Sprintf("(%d).bin", count)); os.IsNotExist(err) {
111-
compressedFile.Name = compressedFile.Name + fmt.Sprintf("(%d).bin", count)
112-
break
113-
}
114-
count++
115-
}
116-
} else if os.IsNotExist(err) {
117-
compressedFile.Name = compressedFile.Name + ".bin"
124+
// Check if the output file already exists; rename if necessary
125+
if _, err := os.Stat(*outputDir + "/" + compressedFile.Name); err == nil {
126+
InvalidateFileName(&compressedFile, outputDir)
118127
}
119128

120129
// Write compressed file to the output directory
121-
err := os.WriteFile(*outputDir+"/"+compressedFile.Name, compressedFile.Content, 0644)
130+
err = os.WriteFile(*outputDir+"/"+compressedFile.Name, compressedFile.Content, 0644)
122131
if err != nil {
123132
return fmt.Errorf("failed to write compressed file: %w", err)
124133
}
125134

135+
//get the file size
136+
compressedFileInfo, err := os.Stat(*outputDir + "/" + compressedFile.Name)
137+
if err != nil {
138+
return fmt.Errorf("failed to get compressed file info: %w", err)
139+
}
140+
141+
compressedSize = compressedFileInfo.Size()
142+
143+
// Calculate compression ratio
144+
compressionRatio := float64(compressedSize) / float64(originalSize)
145+
146+
// Print compression statistics
147+
utils.ColorPrint(utils.GREEN, fmt.Sprintf("Compression complete: Original Size: %s, Compressed Size: %s, Compression Ratio: %.2f%%\n",
148+
utils.FileSize(originalSize), utils.FileSize(compressedSize), compressionRatio*100))
149+
126150
return nil
127151
}
128152

129153
// Function to recursively compress a folder and its contents
130-
func compressFolderRecursive(folderPath string, files *[]utils.File) error {
154+
func compressFolderRecursive(folderPath string, files *[]utils.File, originalSize *int64) error {
131155
// Traverse the folder contents
132156
err := filepath.Walk(folderPath, func(filePath string, info os.FileInfo, err error) error {
133157
if err != nil {
@@ -140,16 +164,19 @@ func compressFolderRecursive(folderPath string, files *[]utils.File) error {
140164
return fmt.Errorf("failed to read file %s: %w", filePath, err)
141165
}
142166

143-
filename, err := filepath.Rel(folderPath, filePath)
167+
// Store file information (relative path and content)
168+
relativePath, err := filepath.Rel(folderPath, filePath)
144169
if err != nil {
145170
return fmt.Errorf("failed to get relative path for file %s: %w", filePath, err)
146171
}
147172

148-
// Store file information (relative path and content)
149173
*files = append(*files, utils.File{
150-
Name: filepath.ToSlash(filename), // Store relative path
174+
Name: filepath.ToSlash(relativePath), // Store relative path
151175
Content: content,
152176
})
177+
178+
// Increment original size and compressed size
179+
atomic.AddInt64(originalSize, info.Size())
153180
}
154181
return nil
155182
})
@@ -161,6 +188,7 @@ func compressFolderRecursive(folderPath string, files *[]utils.File) error {
161188
return nil
162189
}
163190

191+
164192
// Decompress decompresses the specified compressed file into individual files or folders.
165193
func Decompress(filenameStrs []string, outputDir *string, password *string) error {
166194
// Check if there are files to decompress
@@ -188,11 +216,15 @@ func Decompress(filenameStrs []string, outputDir *string, password *string) erro
188216
}
189217

190218
// Decompress file using Huffman coding
191-
files := Unzip(utils.File{
219+
files, err := Unzip(utils.File{
192220
Name: path.Base(filenameStrs[0]),
193221
Content: compressedContent,
194222
})
195223

224+
if err != nil {
225+
return err
226+
}
227+
196228
// Use a wait group to synchronize goroutines
197229
var wg sync.WaitGroup
198230
var errMutex sync.Mutex // Mutex to handle errors safely
@@ -214,13 +246,18 @@ func Decompress(filenameStrs []string, outputDir *string, password *string) erro
214246
return
215247
}
216248

249+
// Check if the file already exists, rename it with file_N
250+
if _, err := os.Stat(filepath.Join(*outputDir, file.Name)); err == nil {
251+
InvalidateFileName(&file, outputDir)
252+
}
253+
217254
// Write decompressed file content
218255
err = os.WriteFile(filepath.Join(*outputDir, file.Name), file.Content, 0644)
219256
if err != nil {
220257
errChan <- fmt.Errorf("failed to write decompressed file %s: %w", file.Name, err)
221258
return
222259
}
223-
utils.ColorPrint(utils.GREEN, fmt.Sprintf("Decompressed file: %s\n", file.Name))
260+
utils.ColorPrint(utils.YELLOW, fmt.Sprintf("Decompressed file: %s\n", file.Name))
224261
}(file)
225262
}
226263

@@ -238,4 +275,20 @@ func Decompress(filenameStrs []string, outputDir *string, password *string) erro
238275
}
239276

240277
return nil
241-
}
278+
}
279+
280+
func InvalidateFileName(file *utils.File, outputDir *string) {
281+
fileExt := path.Ext(file.Name)
282+
//extract the file name without the extension
283+
filename := path.Base(file.Name)
284+
filename = strings.TrimSuffix(filename, fileExt)
285+
count := 1
286+
for {
287+
if _, err := os.Stat(filepath.Join(*outputDir, filepath.Dir(file.Name), filename+fmt.Sprintf("_%d%s", count, fileExt))); os.IsNotExist(err) {
288+
utils.ColorPrint(utils.PURPLE, fmt.Sprintf("File %s already exists, renaming to %s\n", file.Name, filename+fmt.Sprintf("_%d%s", count, fileExt)))
289+
file.Name = filepath.Dir(file.Name) + "/" + filename + fmt.Sprintf("_%d%s", count, fileExt)
290+
break
291+
}
292+
count++
293+
}
294+
}

0 commit comments

Comments
 (0)