request/v2/archives.go

154 lines
3.7 KiB
Go

package request
import (
"archive/tar"
"archive/zip"
"io"
"io/fs"
"os"
"path/filepath"
"strings"
"sirherobrine23.com.br/go-bds/request/descompress"
)
// Extract options
type ExtractOptions struct {
Cwd string // Folder output
Strip int // Remove n components from file extraction
PreserveOwners bool // Preserver user and group if avaible
}
func (opts ExtractOptions) StripPath(name string) string {
if name = filepath.ToSlash(name); name[0] == '/' {
name = name[1:]
}
return filepath.Join(strings.Split(name, "/")[max(0, opts.Strip):]...)
}
// Create request and extract to Cwd folder
func Tar(Url string, ExtractOptions ExtractOptions, RequestOption *Options) error {
res, err := Request(Url, RequestOption)
if err != nil {
return err
}
defer res.Body.Close()
descompressed, err := descompress.NewDescompress(res.Body)
if err != nil {
return err
}
defer descompressed.Close()
linkes := [][2]string{}
tarReader := tar.NewReader(descompressed)
for {
fileHeader, err := tarReader.Next()
if err != nil {
if err == io.EOF {
break
}
return err
}
rootFile := filepath.Join(ExtractOptions.Cwd, ExtractOptions.StripPath(fileHeader.Name))
if _, err := os.Stat(rootFile); err == nil {
continue
} else if rootFile == ExtractOptions.Cwd {
continue
} else if fileHeader.FileInfo().IsDir() {
if err := os.MkdirAll(rootFile, fileHeader.FileInfo().Mode()); err != nil {
return err
}
continue
} else if fileHeader.FileInfo().Mode().Type() == fs.ModeSymlink {
targetPath := filepath.Join(filepath.Dir(rootFile), fileHeader.Linkname)
if filepath.IsAbs(fileHeader.Linkname) {
targetPath = fileHeader.Linkname
}
linkes = append(linkes, [2]string{rootFile, targetPath})
continue
}
if _, err := os.Stat(filepath.Dir(rootFile)); err != nil && os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(rootFile), 0777); err != nil {
return err
}
}
if fileHeader.FileInfo().Mode().IsRegular() {
// Open file or create
localFile, err := os.OpenFile(rootFile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fileHeader.FileInfo().Mode())
if err != nil {
return err
}
_, err = io.CopyN(localFile, tarReader, fileHeader.Size) // Copy data
localFile.Close() // Close file
if err != nil {
return err
}
}
}
// Create linkers
for _, link := range linkes {
if _, err = os.Lstat(link[0]); err != nil && os.IsNotExist(err) {
if err = os.Symlink(link[1], link[0]); err != nil {
return err
}
}
}
return nil
}
// Create request save to temporary file and extract to Cwd folder
func Zip(Url string, ExtractOptions ExtractOptions, RequestOptions *Options) error {
file, _, err := SaveTmp(Url, "", RequestOptions)
if err != nil {
return err
}
defer func() {
file.Close()
os.Remove(file.Name())
}()
stat, err := file.Stat()
if err != nil {
return err
}
zipFile, err := zip.NewReader(file, stat.Size())
if err != nil {
return err
}
// Files targets
fileReader, fileWriter := io.ReadCloser(nil), io.WriteCloser(nil)
for _, zipFile := range zipFile.File {
filepathCwd, fsInfo := filepath.Join(ExtractOptions.Cwd, ExtractOptions.StripPath(zipFile.Name)), zipFile.FileInfo()
if fsInfo.IsDir() {
if err = os.MkdirAll(filepathCwd, fsInfo.Mode()); err != nil {
return err
}
continue
} else if fileReader, err = zipFile.Open(); err != nil {
return err
} else if fileWriter, err = os.OpenFile(filepathCwd, os.O_CREATE|os.O_WRONLY, fsInfo.Mode()); err != nil {
_ = fileReader.Close()
return err
}
_, err = io.CopyN(fileWriter, fileReader, fsInfo.Size())
_ = fileReader.Close()
fileReader = nil
_ = fileWriter.Close()
fileWriter = nil
if err != nil {
return err
}
}
return nil
}