-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompress.go
132 lines (107 loc) · 2.94 KB
/
compress.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// @author: Brian Wojtczak
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"github.com/dustin/go-humanize"
"github.com/google/renameio"
"github.com/klauspost/compress/gzip"
"github.com/pkg/errors"
"io"
"log"
"os"
"path/filepath"
"time"
)
// CompressFile takes a filename and compresses it to a new file of the
// same name with a .gz suffix added. If keep is false, the original file is
// deleted if compression is successful.
func CompressFile(filename, suffix string, level int, keep, force, named, verbose bool) (err error) {
var (
inputInfo os.FileInfo
outputInfo os.FileInfo
inputFile *os.File
outputFile *renameio.PendingFile
zw *gzip.Writer
outputFilename string
)
started := time.Now().UTC()
if verbose {
log.Printf(
"Compressing %s",
filename,
)
}
inputInfo, err = os.Stat(filename)
if err != nil {
return errors.Wrap(err, "error stating input file")
}
inputFile, err = os.Open(filename)
if err != nil {
return errors.Wrap(err, "error opening input file")
}
//goland:noinspection GoUnhandledErrorResult
defer inputFile.Close()
outputFilename = filename + suffix
if !force {
if _, err := os.Stat(outputFilename); err == nil {
return errors.New("output file already exists")
}
}
outputFile, err = renameio.TempFile(filepath.Dir(outputFilename), outputFilename)
if err != nil {
return errors.Wrap(err, "error creating temporary output file")
}
//goland:noinspection GoUnhandledErrorResult
defer outputFile.Cleanup()
outputBuffer := bufio.NewWriter(outputFile)
zw, err = gzip.NewWriterLevel(outputBuffer, level)
if err != nil {
return errors.Wrap(err, "error creating gzip writer")
}
if named {
zw.Name = filepath.Base(filename)
zw.ModTime = inputInfo.ModTime()
}
_, err = io.Copy(zw, bufio.NewReader(inputFile))
if err != nil {
return errors.Wrap(err, "error writing data")
}
if err := inputFile.Close(); err != nil {
return errors.Wrap(err, "error closing input file")
}
if err := zw.Flush(); err != nil {
return errors.Wrap(err, "error flushing data")
}
if err := zw.Close(); err != nil {
return errors.Wrap(err, "error closing output writer")
}
if err := outputBuffer.Flush(); err != nil {
return errors.Wrap(err, "error flushing output buffer")
}
if err := outputFile.CloseAtomicallyReplace(); err != nil {
return errors.Wrap(err, "error atomically closing output file")
}
outputInfo, err = os.Stat(outputFilename)
if err != nil {
return errors.Wrap(err, "error stating output file")
}
if !keep {
if err = os.Remove(filename); err != nil {
return errors.Wrap(err, "error removing input file")
}
}
if verbose {
duration := time.Since(started)
log.Printf(
"Compressed %s from %s to %s (level %d) in %v",
outputFilename,
humanize.Bytes(uint64(inputInfo.Size())),
humanize.Bytes(uint64(outputInfo.Size())),
level,
duration,
)
}
return nil
}