github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/Unknwon/cae/zip/zip.go (about)

     1  // Copyright 2013 Unknown
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  // Package zip enables you to transparently read or write ZIP compressed archives and the files inside them.
    16  package zip
    17  
    18  import (
    19  	"archive/zip"
    20  	"errors"
    21  	"io"
    22  	"os"
    23  	"path"
    24  	"path/filepath"
    25  	"strings"
    26  
    27  	"github.com/insionng/yougam/libraries/Unknwon/cae"
    28  )
    29  
    30  // A File represents a file or directory entry in archive.
    31  type File struct {
    32  	*zip.FileHeader
    33  	oldName    string // NOTE: unused, for future change name feature.
    34  	oldComment string // NOTE: unused, for future change comment feature.
    35  	absPath    string // Absolute path of local file system.
    36  	tmpPath    string
    37  }
    38  
    39  // A ZipArchive represents a file archive, compressed with Zip.
    40  type ZipArchive struct {
    41  	*zip.ReadCloser
    42  	FileName   string
    43  	Comment    string
    44  	NumFiles   int
    45  	Flag       int
    46  	Permission os.FileMode
    47  
    48  	files        []*File
    49  	isHasChanged bool
    50  
    51  	// For supporting flushing to io.Writer.
    52  	writer      io.Writer
    53  	isHasWriter bool
    54  }
    55  
    56  // OpenFile is the generalized open call; most users will use Open
    57  // instead. It opens the named zip file with specified flag
    58  // (O_RDONLY etc.) if applicable. If successful,
    59  // methods on the returned ZipArchive can be used for I/O.
    60  // If there is an error, it will be of type *PathError.
    61  func OpenFile(name string, flag int, perm os.FileMode) (*ZipArchive, error) {
    62  	z := new(ZipArchive)
    63  	err := z.Open(name, flag, perm)
    64  	return z, err
    65  }
    66  
    67  // Create creates the named zip file, truncating
    68  // it if it already exists. If successful, methods on the returned
    69  // ZipArchive can be used for I/O; the associated file descriptor has mode
    70  // O_RDWR.
    71  // If there is an error, it will be of type *PathError.
    72  func Create(name string) (*ZipArchive, error) {
    73  	os.MkdirAll(path.Dir(name), os.ModePerm)
    74  	return OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
    75  }
    76  
    77  // Open opens the named zip file for reading. If successful, methods on
    78  // the returned ZipArchive can be used for reading; the associated file
    79  // descriptor has mode O_RDONLY.
    80  // If there is an error, it will be of type *PathError.
    81  func Open(name string) (*ZipArchive, error) {
    82  	return OpenFile(name, os.O_RDONLY, 0)
    83  }
    84  
    85  // New accepts a variable that implemented interface io.Writer
    86  // for write-only purpose operations.
    87  func New(w io.Writer) *ZipArchive {
    88  	return &ZipArchive{
    89  		writer:      w,
    90  		isHasWriter: true,
    91  	}
    92  }
    93  
    94  // List returns a string slice of files' name in ZipArchive.
    95  // Specify prefixes will be used as filters.
    96  func (z *ZipArchive) List(prefixes ...string) []string {
    97  	isHasPrefix := len(prefixes) > 0
    98  	names := make([]string, 0, z.NumFiles)
    99  	for _, f := range z.files {
   100  		if isHasPrefix && !cae.HasPrefix(f.Name, prefixes) {
   101  			continue
   102  		}
   103  		names = append(names, f.Name)
   104  	}
   105  	return names
   106  }
   107  
   108  // AddEmptyDir adds a raw directory entry to ZipArchive,
   109  // it returns false if same directory enry already existed.
   110  func (z *ZipArchive) AddEmptyDir(dirPath string) bool {
   111  	if !strings.HasSuffix(dirPath, "/") {
   112  		dirPath += "/"
   113  	}
   114  
   115  	for _, f := range z.files {
   116  		if dirPath == f.Name {
   117  			return false
   118  		}
   119  	}
   120  
   121  	dirPath = strings.TrimSuffix(dirPath, "/")
   122  	if strings.Contains(dirPath, "/") {
   123  		// Auto add all upper level directories.
   124  		z.AddEmptyDir(path.Dir(dirPath))
   125  	}
   126  	z.files = append(z.files, &File{
   127  		FileHeader: &zip.FileHeader{
   128  			Name:             dirPath + "/",
   129  			UncompressedSize: 0,
   130  		},
   131  	})
   132  	z.updateStat()
   133  	return true
   134  }
   135  
   136  // AddDir adds a directory and subdirectories entries to ZipArchive.
   137  func (z *ZipArchive) AddDir(dirPath, absPath string) error {
   138  	dir, err := os.Open(absPath)
   139  	if err != nil {
   140  		return err
   141  	}
   142  	defer dir.Close()
   143  
   144  	// Make sure we have all upper level directories.
   145  	z.AddEmptyDir(dirPath)
   146  
   147  	fis, err := dir.Readdir(0)
   148  	if err != nil {
   149  		return err
   150  	}
   151  	for _, fi := range fis {
   152  		curPath := strings.Replace(absPath+"/"+fi.Name(), "\\", "/", -1)
   153  		tmpRecPath := strings.Replace(filepath.Join(dirPath, fi.Name()), "\\", "/", -1)
   154  		if fi.IsDir() {
   155  			if err = z.AddDir(tmpRecPath, curPath); err != nil {
   156  				return err
   157  			}
   158  		} else {
   159  			if err = z.AddFile(tmpRecPath, curPath); err != nil {
   160  				return err
   161  			}
   162  		}
   163  	}
   164  	return nil
   165  }
   166  
   167  // updateStat should be called after every change for rebuilding statistic.
   168  func (z *ZipArchive) updateStat() {
   169  	z.NumFiles = len(z.files)
   170  	z.isHasChanged = true
   171  }
   172  
   173  // AddFile adds a file entry to ZipArchive.
   174  func (z *ZipArchive) AddFile(fileName, absPath string) error {
   175  	if cae.IsFilter(absPath) {
   176  		return nil
   177  	}
   178  
   179  	f, err := os.Open(absPath)
   180  	if err != nil {
   181  		return err
   182  	}
   183  	defer f.Close()
   184  
   185  	fi, err := f.Stat()
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	file := new(File)
   191  	file.FileHeader, err = zip.FileInfoHeader(fi)
   192  	if err != nil {
   193  		return err
   194  	}
   195  	file.Name = fileName
   196  	file.absPath = absPath
   197  
   198  	z.AddEmptyDir(path.Dir(fileName))
   199  
   200  	isExist := false
   201  	for _, f := range z.files {
   202  		if fileName == f.Name {
   203  			f = file
   204  			isExist = true
   205  			break
   206  		}
   207  	}
   208  	if !isExist {
   209  		z.files = append(z.files, file)
   210  	}
   211  
   212  	z.updateStat()
   213  	return nil
   214  }
   215  
   216  // DeleteIndex deletes an entry in the archive by its index.
   217  func (z *ZipArchive) DeleteIndex(idx int) error {
   218  	if idx >= z.NumFiles {
   219  		return errors.New("index out of range of number of files")
   220  	}
   221  
   222  	z.files = append(z.files[:idx], z.files[idx+1:]...)
   223  	return nil
   224  }
   225  
   226  // DeleteName deletes an entry in the archive by its name.
   227  func (z *ZipArchive) DeleteName(name string) error {
   228  	for i, f := range z.files {
   229  		if f.Name == name {
   230  			return z.DeleteIndex(i)
   231  		}
   232  	}
   233  	return errors.New("entry with given name not found")
   234  }