cloud-game/pkg/worker/compression/zip/compression.go
Sergey Stepanov cd056ee976
Bump to v3
2023-03-16 23:46:53 +03:00

134 lines
2.7 KiB
Go

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
}