package zip import ( "archive/zip" "bytes" "errors" "io" "os" "path/filepath" "strings" "github.com/giongto35/cloud-game/v3/pkg/logger" ) const Ext = ".zip" var ( ErrorNotFound = errors.New("not found") ErrorInvalidName = errors.New("invalid name") ) type Extractor struct { log *logger.Logger } func New(log *logger.Logger) Extractor { return Extractor{ log: log, } } // Compress compresses the bytes (a single file) with a name specified into a ZIP file (as bytes). func Compress(data []byte, name string) ([]byte, error) { buf := new(bytes.Buffer) w := zip.NewWriter(buf) //w.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) { // return flate.NewWriter(out, flate.BestCompression) //}) z, err := w.Create(name) if err != nil { return nil, err } _, err = z.Write(data) if err != nil { return nil, err } err = w.Close() if err != nil { return nil, err } return buf.Bytes(), nil } // Read reads a single ZIP file from the bytes array. // It will return un-compressed data and the name of that file. func Read(zd []byte) ([]byte, string, error) { r, err := zip.NewReader(bytes.NewReader(zd), int64(len(zd))) if err != nil { return nil, "", err } for _, f := range r.File { if f.FileInfo().IsDir() { continue } rc, err := f.Open() if err != nil { return nil, "", err } b, err := io.ReadAll(rc) if err != nil { return nil, "", err } if err := rc.Close(); err != nil { return nil, "", err } return b, f.FileInfo().Name(), nil } return nil, "", ErrorNotFound } func (e Extractor) Extract(src string, dest string) (files []string, err error) { r, err := zip.OpenReader(src) if err != nil { return files, err } defer r.Close() for _, f := range r.File { path := filepath.Join(dest, f.Name) // negate ZipSlip vulnerability (http://bit.ly/2MsjAWE) if !strings.HasPrefix(path, filepath.Clean(dest)+string(os.PathSeparator)) { e.log.Warn().Msgf("%s is illegal path", path) continue } // remake directory if f.FileInfo().IsDir() { if err := os.MkdirAll(path, os.ModePerm); err != nil { e.log.Error().Err(err) } continue } // make file if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil { e.log.Error().Err(err) continue } out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { e.log.Error().Err(err) continue } rc, err := f.Open() if err != nil { e.log.Error().Err(err) continue } if _, err = io.Copy(out, rc); err != nil { e.log.Error().Err(err) _ = out.Close() _ = rc.Close() continue } _ = out.Close() _ = rc.Close() files = append(files, path) } return files, nil }