2021-01-17 21:46:25 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
2019-07-24 14:48:16 +08:00
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
|
|
|
|
|
|
|
package gcompress
|
|
|
|
|
|
|
|
import (
|
|
|
|
"archive/zip"
|
2019-07-24 15:05:02 +08:00
|
|
|
"bytes"
|
2021-06-26 16:23:54 +08:00
|
|
|
"context"
|
2019-07-24 14:48:16 +08:00
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2019-08-10 23:36:38 +08:00
|
|
|
"strings"
|
2021-11-13 23:23:55 +08:00
|
|
|
|
|
|
|
"github.com/gogf/gf/v2/internal/intlog"
|
|
|
|
"github.com/gogf/gf/v2/os/gfile"
|
|
|
|
"github.com/gogf/gf/v2/text/gstr"
|
2019-07-24 14:48:16 +08:00
|
|
|
)
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// ZipPath compresses `paths` to `dest` using zip compressing algorithm.
|
|
|
|
// The unnecessary parameter `prefix` indicates the path prefix for zip file.
|
2019-09-03 23:18:54 +08:00
|
|
|
//
|
2021-10-21 18:22:47 +08:00
|
|
|
// Note that the parameter `paths` can be either a directory or a file, which
|
2020-01-15 00:15:56 +08:00
|
|
|
// supports multiple paths join with ','.
|
2019-09-03 23:18:54 +08:00
|
|
|
func ZipPath(paths, dest string, prefix ...string) error {
|
2019-08-10 23:36:38 +08:00
|
|
|
writer, err := os.Create(dest)
|
2019-07-24 14:48:16 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-08-10 23:36:38 +08:00
|
|
|
defer writer.Close()
|
2020-03-06 15:38:32 +08:00
|
|
|
zipWriter := zip.NewWriter(writer)
|
|
|
|
defer zipWriter.Close()
|
|
|
|
for _, path := range strings.Split(paths, ",") {
|
|
|
|
path = strings.TrimSpace(path)
|
|
|
|
if err := doZipPathWriter(path, gfile.RealPath(dest), zipWriter, prefix...); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2019-08-10 23:36:38 +08:00
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// ZipPathWriter compresses `paths` to `writer` using zip compressing algorithm.
|
|
|
|
// The unnecessary parameter `prefix` indicates the path prefix for zip file.
|
2019-09-03 23:18:54 +08:00
|
|
|
//
|
2021-10-21 18:22:47 +08:00
|
|
|
// Note that the parameter `paths` can be either a directory or a file, which
|
2020-01-15 00:15:56 +08:00
|
|
|
// supports multiple paths join with ','.
|
2019-09-03 23:18:54 +08:00
|
|
|
func ZipPathWriter(paths string, writer io.Writer, prefix ...string) error {
|
|
|
|
zipWriter := zip.NewWriter(writer)
|
|
|
|
defer zipWriter.Close()
|
|
|
|
for _, path := range strings.Split(paths, ",") {
|
|
|
|
path = strings.TrimSpace(path)
|
2020-03-06 15:38:32 +08:00
|
|
|
if err := doZipPathWriter(path, "", zipWriter, prefix...); err != nil {
|
2019-09-03 23:18:54 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// doZipPathWriter compresses the file of given `path` and writes the content to `zipWriter`.
|
|
|
|
// The parameter `exclude` specifies the exclusive file path that is not compressed to `zipWriter`,
|
2020-03-06 15:38:32 +08:00
|
|
|
// commonly the destination zip file path.
|
2021-10-21 18:22:47 +08:00
|
|
|
// The unnecessary parameter `prefix` indicates the path prefix for zip file.
|
2020-03-06 15:38:32 +08:00
|
|
|
func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix ...string) error {
|
2020-01-15 00:15:56 +08:00
|
|
|
var err error
|
|
|
|
var files []string
|
2020-03-06 15:38:32 +08:00
|
|
|
path, err = gfile.Search(path)
|
2019-08-10 23:36:38 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-01-15 00:15:56 +08:00
|
|
|
if gfile.IsDir(path) {
|
|
|
|
files, err = gfile.ScanDir(path, "*", true)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
files = []string{path}
|
2019-07-24 14:48:16 +08:00
|
|
|
}
|
|
|
|
headerPrefix := ""
|
2019-09-03 23:18:54 +08:00
|
|
|
if len(prefix) > 0 && prefix[0] != "" {
|
2019-07-24 14:48:16 +08:00
|
|
|
headerPrefix = prefix[0]
|
|
|
|
}
|
2019-09-03 23:18:54 +08:00
|
|
|
headerPrefix = strings.TrimRight(headerPrefix, "\\/")
|
2020-05-01 00:18:45 +08:00
|
|
|
if gfile.IsDir(path) {
|
|
|
|
if len(headerPrefix) > 0 {
|
|
|
|
headerPrefix += "/"
|
|
|
|
} else {
|
|
|
|
headerPrefix = gfile.Basename(path)
|
|
|
|
}
|
|
|
|
|
2019-09-03 17:18:16 +08:00
|
|
|
}
|
2019-09-03 23:18:54 +08:00
|
|
|
headerPrefix = strings.Replace(headerPrefix, "//", "/", -1)
|
2019-07-24 14:48:16 +08:00
|
|
|
for _, file := range files {
|
2020-03-06 15:38:32 +08:00
|
|
|
if exclude == file {
|
2021-06-26 16:23:54 +08:00
|
|
|
intlog.Printf(context.TODO(), `exclude file path: %s`, file)
|
2020-03-06 15:38:32 +08:00
|
|
|
continue
|
|
|
|
}
|
2020-05-01 00:18:45 +08:00
|
|
|
dir := gfile.Dir(file[len(path):])
|
|
|
|
if dir == "." {
|
|
|
|
dir = ""
|
2019-07-24 14:48:16 +08:00
|
|
|
}
|
2020-05-01 00:18:45 +08:00
|
|
|
err := zipFile(file, headerPrefix+dir, zipWriter)
|
2019-09-03 23:18:54 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2019-07-24 14:48:16 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// UnZipFile decompresses `archive` to `dest` using zip compressing algorithm.
|
|
|
|
// The optional parameter `path` specifies the unzipped path of `archive`,
|
2019-07-24 15:58:56 +08:00
|
|
|
// which can be used to specify part of the archive file to unzip.
|
2019-09-03 23:18:54 +08:00
|
|
|
//
|
2021-10-21 18:22:47 +08:00
|
|
|
// Note that the parameter `dest` should be a directory.
|
2019-07-24 15:58:56 +08:00
|
|
|
func UnZipFile(archive, dest string, path ...string) error {
|
2019-07-24 15:05:02 +08:00
|
|
|
readerCloser, err := zip.OpenReader(archive)
|
2019-07-24 14:48:16 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-24 15:05:02 +08:00
|
|
|
defer readerCloser.Close()
|
2019-07-24 15:58:56 +08:00
|
|
|
return unZipFileWithReader(&readerCloser.Reader, dest, path...)
|
2019-07-24 15:05:02 +08:00
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// UnZipContent decompresses `data` to `dest` using zip compressing algorithm.
|
|
|
|
// The parameter `path` specifies the unzipped path of `archive`,
|
2019-07-24 15:58:56 +08:00
|
|
|
// which can be used to specify part of the archive file to unzip.
|
2019-09-03 23:18:54 +08:00
|
|
|
//
|
2021-10-21 18:22:47 +08:00
|
|
|
// Note that the parameter `dest` should be a directory.
|
2019-07-24 15:58:56 +08:00
|
|
|
func UnZipContent(data []byte, dest string, path ...string) error {
|
2019-07-24 15:05:02 +08:00
|
|
|
reader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-24 15:58:56 +08:00
|
|
|
return unZipFileWithReader(reader, dest, path...)
|
2019-07-24 15:05:02 +08:00
|
|
|
}
|
|
|
|
|
2019-07-24 15:58:56 +08:00
|
|
|
func unZipFileWithReader(reader *zip.Reader, dest string, path ...string) error {
|
|
|
|
prefix := ""
|
|
|
|
if len(path) > 0 {
|
|
|
|
prefix = gstr.Replace(path[0], `\`, `/`)
|
|
|
|
}
|
2019-07-24 14:48:16 +08:00
|
|
|
if err := os.MkdirAll(dest, 0755); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-24 15:58:56 +08:00
|
|
|
name := ""
|
2019-07-24 14:48:16 +08:00
|
|
|
for _, file := range reader.File {
|
2019-07-24 15:58:56 +08:00
|
|
|
name = gstr.Replace(file.Name, `\`, `/`)
|
|
|
|
name = gstr.Trim(name, "/")
|
|
|
|
if prefix != "" {
|
|
|
|
if name[0:len(prefix)] != prefix {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
name = name[len(prefix):]
|
|
|
|
}
|
|
|
|
path := filepath.Join(dest, name)
|
2019-07-24 14:48:16 +08:00
|
|
|
if file.FileInfo().IsDir() {
|
|
|
|
os.MkdirAll(path, file.Mode())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
dir := filepath.Dir(path)
|
|
|
|
if len(dir) > 0 {
|
2019-07-24 15:05:02 +08:00
|
|
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
2019-07-24 14:48:16 +08:00
|
|
|
err = os.MkdirAll(dir, 0755)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fileReader, err := file.Open()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer fileReader.Close()
|
|
|
|
|
|
|
|
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer targetFile.Close()
|
|
|
|
|
|
|
|
if _, err := io.Copy(targetFile, fileReader); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-10-21 18:22:47 +08:00
|
|
|
// zipFile compresses the file of given `path` and writes the content to `zw`.
|
|
|
|
// The parameter `prefix` indicates the path prefix for zip file.
|
2019-07-24 14:48:16 +08:00
|
|
|
func zipFile(path string, prefix string, zw *zip.Writer) error {
|
|
|
|
file, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
defer file.Close()
|
2020-05-01 00:18:45 +08:00
|
|
|
|
2019-07-24 14:48:16 +08:00
|
|
|
info, err := file.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-01 00:18:45 +08:00
|
|
|
|
2019-09-03 23:18:54 +08:00
|
|
|
header, err := createFileHeader(info, prefix)
|
2019-07-24 14:48:16 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-05-01 00:18:45 +08:00
|
|
|
|
|
|
|
if info.IsDir() {
|
|
|
|
header.Name += "/"
|
|
|
|
} else {
|
|
|
|
header.Method = zip.Deflate
|
|
|
|
}
|
|
|
|
|
2019-07-24 14:48:16 +08:00
|
|
|
writer, err := zw.CreateHeader(header)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-08-10 23:36:38 +08:00
|
|
|
if !info.IsDir() {
|
|
|
|
if _, err = io.Copy(writer, file); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-24 14:48:16 +08:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2019-09-03 23:18:54 +08:00
|
|
|
|
|
|
|
func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error) {
|
|
|
|
header, err := zip.FileInfoHeader(info)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2020-05-01 00:18:45 +08:00
|
|
|
|
2019-09-03 23:18:54 +08:00
|
|
|
if len(prefix) > 0 {
|
|
|
|
prefix = strings.Replace(prefix, `\`, `/`, -1)
|
|
|
|
prefix = strings.TrimRight(prefix, `/`)
|
|
|
|
header.Name = prefix + `/` + header.Name
|
|
|
|
}
|
|
|
|
return header, nil
|
|
|
|
}
|