github.com/gogf/gf@v1.16.9/os/gres/gres_func_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 gres
     8  
     9  import (
    10  	"archive/zip"
    11  	"context"
    12  	"github.com/gogf/gf/internal/fileinfo"
    13  	"github.com/gogf/gf/internal/intlog"
    14  	"github.com/gogf/gf/os/gfile"
    15  	"github.com/gogf/gf/text/gregex"
    16  	"io"
    17  	"os"
    18  	"strings"
    19  	"time"
    20  )
    21  
    22  // ZipPathWriter compresses <paths> to <writer> 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 zipPathWriter(paths string, writer io.Writer, prefix ...string) error {
    28  	zipWriter := zip.NewWriter(writer)
    29  	defer zipWriter.Close()
    30  	for _, path := range strings.Split(paths, ",") {
    31  		path = strings.TrimSpace(path)
    32  		if err := doZipPathWriter(path, "", zipWriter, prefix...); err != nil {
    33  			return err
    34  		}
    35  	}
    36  	return nil
    37  }
    38  
    39  // doZipPathWriter compresses the file of given <path> and writes the content to <zipWriter>.
    40  // The parameter <exclude> specifies the exclusive file path that is not compressed to <zipWriter>,
    41  // commonly the destination zip file path.
    42  // The unnecessary parameter <prefix> indicates the path prefix for zip file.
    43  func doZipPathWriter(path string, exclude string, zipWriter *zip.Writer, prefix ...string) error {
    44  	var (
    45  		err   error
    46  		files []string
    47  	)
    48  	path, err = gfile.Search(path)
    49  	if err != nil {
    50  		return err
    51  	}
    52  	if gfile.IsDir(path) {
    53  		files, err = gfile.ScanDir(path, "*", true)
    54  		if err != nil {
    55  			return err
    56  		}
    57  	} else {
    58  		files = []string{path}
    59  	}
    60  	headerPrefix := ""
    61  	if len(prefix) > 0 && prefix[0] != "" {
    62  		headerPrefix = prefix[0]
    63  	}
    64  	headerPrefix = strings.TrimRight(headerPrefix, `\/`)
    65  	if len(headerPrefix) > 0 && gfile.IsDir(path) {
    66  		headerPrefix += "/"
    67  	}
    68  	if headerPrefix == "" {
    69  		headerPrefix = gfile.Basename(path)
    70  	}
    71  	headerPrefix = strings.Replace(headerPrefix, `//`, `/`, -1)
    72  	for _, file := range files {
    73  		if exclude == file {
    74  			intlog.Printf(context.TODO(), `exclude file path: %s`, file)
    75  			continue
    76  		}
    77  		err = zipFile(file, headerPrefix+gfile.Dir(file[len(path):]), zipWriter)
    78  		if err != nil {
    79  			return err
    80  		}
    81  	}
    82  	// Add all directories to zip archive.
    83  	if headerPrefix != "" {
    84  		var name string
    85  		path = headerPrefix
    86  		for {
    87  			name = strings.Replace(gfile.Basename(path), `\`, `/`, -1)
    88  			err = zipFileVirtual(
    89  				fileinfo.New(name, 0, os.ModeDir|os.ModePerm, time.Now()), path, zipWriter,
    90  			)
    91  			if err != nil {
    92  				return err
    93  			}
    94  			if path == `/` || !strings.Contains(path, `/`) {
    95  				break
    96  			}
    97  			path = gfile.Dir(path)
    98  		}
    99  	}
   100  	return nil
   101  }
   102  
   103  // zipFile compresses the file of given <path> and writes the content to <zw>.
   104  // The parameter <prefix> indicates the path prefix for zip file.
   105  func zipFile(path string, prefix string, zw *zip.Writer) error {
   106  	prefix = strings.Replace(prefix, `//`, `/`, -1)
   107  	file, err := os.Open(path)
   108  	if err != nil {
   109  		return nil
   110  	}
   111  	defer file.Close()
   112  	info, err := file.Stat()
   113  	if err != nil {
   114  		return err
   115  	}
   116  	header, err := createFileHeader(info, prefix)
   117  	if err != nil {
   118  		return err
   119  	}
   120  	if !info.IsDir() {
   121  		header.Method = zip.Deflate
   122  	}
   123  	writer, err := zw.CreateHeader(header)
   124  	if err != nil {
   125  		return err
   126  	}
   127  	if !info.IsDir() {
   128  		if _, err = io.Copy(writer, file); err != nil {
   129  			return err
   130  		}
   131  	}
   132  	return nil
   133  }
   134  
   135  func zipFileVirtual(info os.FileInfo, path string, zw *zip.Writer) error {
   136  	header, err := createFileHeader(info, "")
   137  	if err != nil {
   138  		return err
   139  	}
   140  	header.Name = path
   141  	if _, err = zw.CreateHeader(header); err != nil {
   142  		return err
   143  	}
   144  	return nil
   145  }
   146  
   147  func createFileHeader(info os.FileInfo, prefix string) (*zip.FileHeader, error) {
   148  	header, err := zip.FileInfoHeader(info)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	if len(prefix) > 0 {
   153  		header.Name = prefix + `/` + header.Name
   154  		header.Name = strings.Replace(header.Name, `\`, `/`, -1)
   155  		header.Name, _ = gregex.ReplaceString(`/{2,}`, `/`, header.Name)
   156  	}
   157  	return header, nil
   158  }