github.com/gogf/gf@v1.16.9/encoding/gcompress/gcompress_zip.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/gogf/gf.
     6  
     7  package gcompress
     8  
     9  import (
    10  	"archive/zip"
    11  	"bytes"
    12  	"context"
    13  	"github.com/gogf/gf/internal/intlog"
    14  	"github.com/gogf/gf/os/gfile"
    15  	"github.com/gogf/gf/text/gstr"
    16  	"io"
    17  	"os"
    18  	"path/filepath"
    19  	"strings"
    20  )
    21  
    22  // ZipPath compresses <paths> to <dest> using zip compressing algorithm.
    23  // The unnecessary parameter <prefix> indicates the path prefix for zip file.
    24  //
    25  // Note that the parameter <paths> can be either a directory or a file, which
    26  // supports multiple paths join with ','.
    27  func ZipPath(paths, dest string, prefix ...string) error {
    28  	writer, err := os.Create(dest)
    29  	if err != nil {
    30  		return err
    31  	}
    32  	defer writer.Close()
    33  	zipWriter := zip.NewWriter(writer)
    34  	defer zipWriter.Close()
    35  	for _, path := range strings.Split(paths, ",") {
    36  		path = strings.TrimSpace(path)
    37  		if err := doZipPathWriter(path, gfile.RealPath(dest), zipWriter, prefix...); err != nil {
    38  			return err
    39  		}
    40  	}
    41  	return nil
    42  }
    43  
    44  // ZipPathWriter compresses <paths> to <writer> using zip compressing algorithm.
    45  // The unnecessary parameter <prefix> indicates the path prefix for zip file.
    46  //
    47  // Note that the parameter <paths> can be either a directory or a file, which
    48  // supports multiple paths join with ','.
    49  func ZipPathWriter(paths string, writer io.Writer, prefix ...string) error {
    50  	zipWriter := zip.NewWriter(writer)
    51  	defer zipWriter.Close()
    52  	for _, path := range strings.Split(paths, ",") {
    53  		path = strings.TrimSpace(path)
    54  		if err := doZipPathWriter(path, "", zipWriter, prefix...); err != nil {
    55  			return err
    56  		}
    57  	}
    58  	return nil
    59  }
    60  
    61  // doZipPathWriter compresses the file of given <path> and writes the content to <zipWriter>.
    62  // The parameter <exclude> specifies the exclusive file path that is not compressed to <zipWriter>,
    63  // commonly the destination zip file path.
    64  // The unnecessary parameter <prefix> indicates the path prefix for zip file.
    65  func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix ...string) error {
    66  	var err error
    67  	var files []string
    68  	path, err = gfile.Search(path)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	if gfile.IsDir(path) {
    73  		files, err = gfile.ScanDir(path, "*", true)
    74  		if err != nil {
    75  			return err
    76  		}
    77  	} else {
    78  		files = []string{path}
    79  	}
    80  	headerPrefix := ""
    81  	if len(prefix) > 0 && prefix[0] != "" {
    82  		headerPrefix = prefix[0]
    83  	}
    84  	headerPrefix = strings.TrimRight(headerPrefix, "\\/")
    85  	if gfile.IsDir(path) {
    86  		if len(headerPrefix) > 0 {
    87  			headerPrefix += "/"
    88  		} else {
    89  			headerPrefix = gfile.Basename(path)
    90  		}
    91  
    92  	}
    93  	headerPrefix = strings.Replace(headerPrefix, "//", "/", -1)
    94  	for _, file := range files {
    95  		if exclude == file {
    96  			intlog.Printf(context.TODO(), `exclude file path: %s`, file)
    97  			continue
    98  		}
    99  		dir := gfile.Dir(file[len(path):])
   100  		if dir == "." {
   101  			dir = ""
   102  		}
   103  		err := zipFile(file, headerPrefix+dir, zipWriter)
   104  		if err != nil {
   105  			return err
   106  		}
   107  	}
   108  	return nil
   109  }
   110  
   111  // UnZipFile decompresses <archive> to <dest> using zip compressing algorithm.
   112  // The optional parameter <path> specifies the unzipped path of <archive>,
   113  // which can be used to specify part of the archive file to unzip.
   114  //
   115  // Note that the parameter <dest> should be a directory.
   116  func UnZipFile(archive, dest string, path ...string) error {
   117  	readerCloser, err := zip.OpenReader(archive)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	defer readerCloser.Close()
   122  	return unZipFileWithReader(&readerCloser.Reader, dest, path...)
   123  }
   124  
   125  // UnZipContent decompresses <data> to <dest> using zip compressing algorithm.
   126  // The parameter <path> specifies the unzipped path of <archive>,
   127  // which can be used to specify part of the archive file to unzip.
   128  //
   129  // Note that the parameter <dest> should be a directory.
   130  func UnZipContent(data []byte, dest string, path ...string) error {
   131  	reader, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))
   132  	if err != nil {
   133  		return err
   134  	}
   135  	return unZipFileWithReader(reader, dest, path...)
   136  }
   137  
   138  func unZipFileWithReader(reader *zip.Reader, dest string, path ...string) error {
   139  	prefix := ""
   140  	if len(path) > 0 {
   141  		prefix = gstr.Replace(path[0], `\`, `/`)
   142  	}
   143  	if err := os.MkdirAll(dest, 0755); err != nil {
   144  		return err
   145  	}
   146  	name := ""
   147  	for _, file := range reader.File {
   148  		name = gstr.Replace(file.Name, `\`, `/`)
   149  		name = gstr.Trim(name, "/")
   150  		if prefix != "" {
   151  			if name[0:len(prefix)] != prefix {
   152  				continue
   153  			}
   154  			name = name[len(prefix):]
   155  		}
   156  		path := filepath.Join(dest, name)
   157  		if file.FileInfo().IsDir() {
   158  			os.MkdirAll(path, file.Mode())
   159  			continue
   160  		}
   161  		dir := filepath.Dir(path)
   162  		if len(dir) > 0 {
   163  			if _, err := os.Stat(dir); os.IsNotExist(err) {
   164  				err = os.MkdirAll(dir, 0755)
   165  				if err != nil {
   166  					return err
   167  				}
   168  			}
   169  		}
   170  		fileReader, err := file.Open()
   171  		if err != nil {
   172  			return err
   173  		}
   174  		defer fileReader.Close()
   175  
   176  		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
   177  		if err != nil {
   178  			return err
   179  		}
   180  		defer targetFile.Close()
   181  
   182  		if _, err := io.Copy(targetFile, fileReader); err != nil {
   183  			return err
   184  		}
   185  	}
   186  	return nil
   187  }
   188  
   189  // zipFile compresses the file of given <path> and writes the content to <zw>.
   190  // The parameter <prefix> indicates the path prefix for zip file.
   191  func zipFile(path string, prefix string, zw *zip.Writer) error {
   192  	file, err := os.Open(path)
   193  	if err != nil {
   194  		return nil
   195  	}
   196  	defer file.Close()
   197  
   198  	info, err := file.Stat()
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	header, err := createFileHeader(info, prefix)
   204  	if err != nil {
   205  		return err
   206  	}
   207  
   208  	if info.IsDir() {
   209  		header.Name += "/"
   210  	} else {
   211  		header.Method = zip.Deflate
   212  	}
   213  
   214  	writer, err := zw.CreateHeader(header)
   215  	if err != nil {
   216  		return err
   217  	}
   218  	if !info.IsDir() {
   219  		if _, err = io.Copy(writer, file); err != nil {
   220  			return err
   221  		}
   222  	}
   223  	return nil
   224  }
   225  
   226  func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error) {
   227  	header, err := zip.FileInfoHeader(info)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  
   232  	if len(prefix) > 0 {
   233  		prefix = strings.Replace(prefix, `\`, `/`, -1)
   234  		prefix = strings.TrimRight(prefix, `/`)
   235  		header.Name = prefix + `/` + header.Name
   236  	}
   237  	return header, nil
   238  }