164 lines
2.9 KiB
Go
164 lines
2.9 KiB
Go
|
|
package zipx
|
||
|
|
|
||
|
|
import (
|
||
|
|
"archive/zip"
|
||
|
|
"context"
|
||
|
|
"io"
|
||
|
|
"io/fs"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
|
||
|
|
"git.cloudyne.io/go/hiscaler-gox/filex"
|
||
|
|
"golang.org/x/sync/errgroup"
|
||
|
|
)
|
||
|
|
|
||
|
|
type zipFile struct {
|
||
|
|
header *zip.FileHeader
|
||
|
|
data *os.File
|
||
|
|
}
|
||
|
|
|
||
|
|
// Compress compresses files and saved, if compactDirectory is true, then will remove all directory path
|
||
|
|
func Compress(filename string, files []string, method uint16, compactDirectory bool) error {
|
||
|
|
zFile, err := os.Create(filename)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer zFile.Close()
|
||
|
|
zipWriter := zip.NewWriter(zFile)
|
||
|
|
defer zipWriter.Close()
|
||
|
|
|
||
|
|
zipFiles := make([]zipFile, len(files))
|
||
|
|
errGrp, ctx := errgroup.WithContext(context.Background())
|
||
|
|
for i, file := range files {
|
||
|
|
f := file
|
||
|
|
j := i
|
||
|
|
errGrp.Go(func() error {
|
||
|
|
select {
|
||
|
|
case <-ctx.Done():
|
||
|
|
return nil
|
||
|
|
default:
|
||
|
|
zf, e := addFile(f, method, compactDirectory)
|
||
|
|
if e != nil {
|
||
|
|
ctx.Done()
|
||
|
|
return e
|
||
|
|
}
|
||
|
|
zipFiles[j] = zf
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
err = errGrp.Wait()
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
for i := range zipFiles {
|
||
|
|
if zipFiles[i].data == nil {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
err = func(i int) error {
|
||
|
|
defer zipFiles[i].data.Close()
|
||
|
|
if err != nil {
|
||
|
|
return err // For close all opened files
|
||
|
|
}
|
||
|
|
writer, e := zipWriter.CreateHeader(zipFiles[i].header)
|
||
|
|
if e != nil {
|
||
|
|
return e
|
||
|
|
}
|
||
|
|
|
||
|
|
_, e = io.Copy(writer, zipFiles[i].data)
|
||
|
|
return e
|
||
|
|
}(i)
|
||
|
|
}
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func addFile(filename string, method uint16, compactDirectory bool) (zipFile zipFile, err error) {
|
||
|
|
pendingAddFile, err := os.Open(filename)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
defer pendingAddFile.Close()
|
||
|
|
|
||
|
|
zipFile.data = pendingAddFile
|
||
|
|
info, err := pendingAddFile.Stat()
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
header, err := zip.FileInfoHeader(info)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if compactDirectory {
|
||
|
|
header.Name = filepath.Base(filename)
|
||
|
|
} else {
|
||
|
|
header.Name = filename
|
||
|
|
}
|
||
|
|
header.Method = method
|
||
|
|
zipFile.header = header
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// UnCompress unzip source file to destination directory
|
||
|
|
func UnCompress(src, dst string) error {
|
||
|
|
r, err := zip.OpenReader(src)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer r.Close()
|
||
|
|
|
||
|
|
// Create destination directory if not exists
|
||
|
|
if !filex.Exists(dst) {
|
||
|
|
err = os.MkdirAll(dst, fs.ModePerm)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, file := range r.File {
|
||
|
|
path := filepath.Join(dst, file.Name)
|
||
|
|
if file.FileInfo().IsDir() {
|
||
|
|
if err = os.MkdirAll(path, file.Mode()); err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
dir := filepath.Dir(path)
|
||
|
|
if !filex.Exists(dir) {
|
||
|
|
err = os.MkdirAll(dir, fs.ModePerm)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if err = writeFile(file, path); err != nil {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
func writeFile(file *zip.File, path string) error {
|
||
|
|
fr, err := file.Open()
|
||
|
|
if err != nil {
|
||
|
|
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
defer fr.Close()
|
||
|
|
fw, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
defer fw.Close()
|
||
|
|
|
||
|
|
_, err = io.Copy(fw, fr)
|
||
|
|
return err
|
||
|
|
}
|