147 lines
2.7 KiB
Go
147 lines
2.7 KiB
Go
|
package bundle
|
||
|
|
||
|
import (
|
||
|
"archive/tar"
|
||
|
"compress/gzip"
|
||
|
"context"
|
||
|
"io"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/pkg/errors"
|
||
|
"gitlab.com/wpetit/goweb/logger"
|
||
|
)
|
||
|
|
||
|
type TarBundle struct {
|
||
|
archivePath string
|
||
|
}
|
||
|
|
||
|
func (b *TarBundle) File(filename string) (io.ReadCloser, os.FileInfo, error) {
|
||
|
reader, archive, err := b.openArchive()
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
|
||
|
ctx := logger.With(
|
||
|
context.Background(),
|
||
|
logger.F("filename", filename),
|
||
|
)
|
||
|
|
||
|
logger.Debug(ctx, "opening file")
|
||
|
|
||
|
for {
|
||
|
header, err := reader.Next()
|
||
|
|
||
|
if err == io.EOF {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
return nil, nil, errors.Wrap(err, "could not get next tar file")
|
||
|
}
|
||
|
|
||
|
p := strings.TrimPrefix(strings.TrimSuffix(header.Name, "/"), "./")
|
||
|
|
||
|
logger.Debug(ctx, "reading archive file", logger.F("path", p))
|
||
|
|
||
|
if filename != p {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if header.Typeflag != tar.TypeReg && header.Typeflag != tar.TypeDir {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
rc := &archiveFile{reader, archive}
|
||
|
|
||
|
return rc, header.FileInfo(), nil
|
||
|
}
|
||
|
|
||
|
return nil, nil, os.ErrNotExist
|
||
|
}
|
||
|
|
||
|
func (b *TarBundle) Dir(dirname string) ([]os.FileInfo, error) {
|
||
|
reader, archive, err := b.openArchive()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
defer archive.Close()
|
||
|
|
||
|
files := make([]os.FileInfo, 0)
|
||
|
ctx := context.Background()
|
||
|
|
||
|
for {
|
||
|
header, err := reader.Next()
|
||
|
|
||
|
if errors.Is(err, io.EOF) {
|
||
|
break
|
||
|
}
|
||
|
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "could not get next tar file")
|
||
|
}
|
||
|
|
||
|
if header.Typeflag != tar.TypeReg && header.Typeflag != tar.TypeDir {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if !strings.HasPrefix(header.Name, dirname) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
relPath, err := filepath.Rel(dirname, header.Name)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "could not get relative path")
|
||
|
}
|
||
|
|
||
|
logger.Debug(
|
||
|
ctx, "checking file prefix",
|
||
|
logger.F("dirname", dirname),
|
||
|
logger.F("filename", header.Name),
|
||
|
logger.F("relpath", relPath),
|
||
|
)
|
||
|
|
||
|
if relPath == filepath.Base(header.Name) {
|
||
|
files = append(files, header.FileInfo())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return files, nil
|
||
|
}
|
||
|
|
||
|
func (b *TarBundle) openArchive() (*tar.Reader, *os.File, error) {
|
||
|
f, err := os.Open(b.archivePath)
|
||
|
if err != nil {
|
||
|
return nil, nil, errors.Wrapf(err, "could not open '%v'", b.archivePath)
|
||
|
}
|
||
|
|
||
|
gzf, err := gzip.NewReader(f)
|
||
|
if err != nil {
|
||
|
return nil, nil, errors.Wrapf(err, "could not decompress '%v'", b.archivePath)
|
||
|
}
|
||
|
|
||
|
tr := tar.NewReader(gzf)
|
||
|
|
||
|
return tr, f, nil
|
||
|
}
|
||
|
|
||
|
func NewTarBundle(archivePath string) *TarBundle {
|
||
|
return &TarBundle{
|
||
|
archivePath: archivePath,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type archiveFile struct {
|
||
|
reader io.Reader
|
||
|
closer io.Closer
|
||
|
}
|
||
|
|
||
|
func (f *archiveFile) Read(p []byte) (n int, err error) {
|
||
|
return f.reader.Read(p)
|
||
|
}
|
||
|
|
||
|
func (f *archiveFile) Close() error {
|
||
|
return f.closer.Close()
|
||
|
}
|