github.com/kaydxh/golang@v0.0.131/go/archive/zip/zip.go (about)

     1  /*
     2   *Copyright (c) 2022, kaydxh
     3   *
     4   *Permission is hereby granted, free of charge, to any person obtaining a copy
     5   *of this software and associated documentation files (the "Software"), to deal
     6   *in the Software without restriction, including without limitation the rights
     7   *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   *copies of the Software, and to permit persons to whom the Software is
     9   *furnished to do so, subject to the following conditions:
    10   *
    11   *The above copyright notice and this permission notice shall be included in all
    12   *copies or substantial portions of the Software.
    13   *
    14   *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   *SOFTWARE.
    21   */
    22  package zip
    23  
    24  import (
    25  	"archive/zip"
    26  	"bytes"
    27  	"fmt"
    28  	"io"
    29  	"io/ioutil"
    30  	"path/filepath"
    31  	"strings"
    32  	"unicode/utf8"
    33  
    34  	"github.com/kaydxh/golang/go/archive/option"
    35  	os_ "github.com/kaydxh/golang/go/os"
    36  	"golang.org/x/text/encoding/simplifiedchinese"
    37  	"golang.org/x/text/transform"
    38  )
    39  
    40  type COPY_TYPE int
    41  
    42  type Zip struct {
    43  }
    44  
    45  func (z Zip) Extract(srcFile, destDir string) ([]*option.FileInfo, error) {
    46  	r, err := zip.OpenReader(srcFile)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  	defer r.Close()
    51  
    52  	err = os_.MakeDirAll(destDir)
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	var extractedFiles []*option.FileInfo
    58  	for _, f := range r.File {
    59  		fileInfo, err := z.extractAndWriteFile(destDir, f)
    60  		if err != nil {
    61  			return nil, err
    62  		}
    63  
    64  		extractedFiles = append(extractedFiles, fileInfo)
    65  	}
    66  
    67  	return extractedFiles, nil
    68  }
    69  
    70  func (z Zip) ExtractStream(
    71  	srcFile, destDir string,
    72  ) <-chan option.ExtractMsg {
    73  
    74  	fileInfoCh := make(chan option.ExtractMsg, 1024)
    75  
    76  	go func() error {
    77  		defer close(fileInfoCh)
    78  		r, err := zip.OpenReader(srcFile)
    79  		if err != nil {
    80  			return err
    81  		}
    82  		defer r.Close()
    83  
    84  		err = os_.MakeDirAll(destDir)
    85  		if err != nil {
    86  			return err
    87  		}
    88  
    89  		for _, f := range r.File {
    90  			fileInfo, err := z.extractAndWriteFile(destDir, f)
    91  			if err != nil {
    92  				fileInfoCh <- option.ExtractMsg{
    93  					Error: err,
    94  				}
    95  				return err
    96  			}
    97  
    98  			if fileInfo != nil {
    99  				fileInfoCh <- option.ExtractMsg{
   100  					FileInfo: fileInfo,
   101  					Error:    err,
   102  				}
   103  				return nil
   104  			}
   105  
   106  		}
   107  		return nil
   108  	}()
   109  
   110  	return fileInfoCh
   111  }
   112  
   113  func (z Zip) extractAndWriteFile(
   114  	destDir string,
   115  	f *zip.File,
   116  ) (*option.FileInfo, error) {
   117  
   118  	if f == nil {
   119  		return nil, fmt.Errorf("invalid zip file")
   120  	}
   121  
   122  	decodeName := f.Name
   123  	if !utf8.Valid([]byte(f.Name)) {
   124  		i := bytes.NewReader([]byte(f.Name))
   125  		decoder := transform.NewReader(
   126  			i,
   127  			simplifiedchinese.GB18030.NewDecoder(),
   128  		)
   129  		content, err := ioutil.ReadAll(decoder)
   130  		if err != nil {
   131  			return nil, err
   132  		}
   133  		decodeName = string(content)
   134  	}
   135  
   136  	baseName := filepath.Base(f.Name)
   137  	if strings.HasPrefix(baseName, ".") {
   138  		return nil, nil
   139  	}
   140  
   141  	rc, err := f.Open()
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	cleanFunc := func() error {
   146  		if err = rc.Close(); err != nil {
   147  			return err
   148  		}
   149  
   150  		return nil
   151  	}
   152  	defer cleanFunc()
   153  
   154  	path := filepath.Join(destDir, decodeName)
   155  	if f.FileInfo().IsDir() {
   156  		err = os_.MakeDirAll(path)
   157  		if err != nil {
   158  			return nil, err
   159  		}
   160  
   161  		return nil, nil
   162  	}
   163  
   164  	fn, err := os_.OpenFile(path, false)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	defer fn.Close()
   169  
   170  	_, err = io.Copy(fn, rc)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  
   175  	return &option.FileInfo{
   176  		Path:     fn.Name(),
   177  		FileInfo: f.FileInfo(),
   178  	}, nil
   179  }