* feat(archive): multipart support & sevenzip tool * feat(archive): rardecode tool * feat(archive): support decompress multi-selected * fix(archive): decompress response filter internal * feat(archive): support multipart zip * fix: more applicable AcceptedMultipartExtensions interface
This commit is contained in:
@ -3,6 +3,7 @@ package op
|
||||
import (
|
||||
"context"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
stdpath "path"
|
||||
"strings"
|
||||
@ -54,21 +55,76 @@ func GetArchiveMeta(ctx context.Context, storage driver.Driver, path string, arg
|
||||
return meta, err
|
||||
}
|
||||
|
||||
func getArchiveToolAndStream(ctx context.Context, storage driver.Driver, path string, args model.LinkArgs) (model.Obj, tool.Tool, *stream.SeekableStream, error) {
|
||||
func GetArchiveToolAndStream(ctx context.Context, storage driver.Driver, path string, args model.LinkArgs) (model.Obj, tool.Tool, []*stream.SeekableStream, error) {
|
||||
l, obj, err := Link(ctx, storage, path, args)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.WithMessagef(err, "failed get [%s] link", path)
|
||||
}
|
||||
ext := stdpath.Ext(obj.GetName())
|
||||
t, err := tool.GetArchiveTool(ext)
|
||||
baseName, ext, found := strings.Cut(obj.GetName(), ".")
|
||||
if !found {
|
||||
if l.MFile != nil {
|
||||
_ = l.MFile.Close()
|
||||
}
|
||||
if l.RangeReadCloser != nil {
|
||||
_ = l.RangeReadCloser.Close()
|
||||
}
|
||||
return nil, nil, nil, errors.Errorf("failed get archive tool: the obj does not have an extension.")
|
||||
}
|
||||
partExt, t, err := tool.GetArchiveTool("." + ext)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.WithMessagef(err, "failed get [%s] archive tool", ext)
|
||||
var e error
|
||||
partExt, t, e = tool.GetArchiveTool(stdpath.Ext(obj.GetName()))
|
||||
if e != nil {
|
||||
if l.MFile != nil {
|
||||
_ = l.MFile.Close()
|
||||
}
|
||||
if l.RangeReadCloser != nil {
|
||||
_ = l.RangeReadCloser.Close()
|
||||
}
|
||||
return nil, nil, nil, errors.WithMessagef(stderrors.Join(err, e), "failed get archive tool: %s", ext)
|
||||
}
|
||||
}
|
||||
ss, err := stream.NewSeekableStream(stream.FileStream{Ctx: ctx, Obj: obj}, l)
|
||||
if err != nil {
|
||||
if l.MFile != nil {
|
||||
_ = l.MFile.Close()
|
||||
}
|
||||
if l.RangeReadCloser != nil {
|
||||
_ = l.RangeReadCloser.Close()
|
||||
}
|
||||
return nil, nil, nil, errors.WithMessagef(err, "failed get [%s] stream", path)
|
||||
}
|
||||
return obj, t, ss, nil
|
||||
ret := []*stream.SeekableStream{ss}
|
||||
if partExt == nil {
|
||||
return obj, t, ret, nil
|
||||
} else {
|
||||
index := partExt.SecondPartIndex
|
||||
dir := stdpath.Dir(path)
|
||||
for {
|
||||
p := stdpath.Join(dir, baseName+fmt.Sprintf(partExt.PartFileFormat, index))
|
||||
var o model.Obj
|
||||
l, o, err = Link(ctx, storage, p, args)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
ss, err = stream.NewSeekableStream(stream.FileStream{Ctx: ctx, Obj: o}, l)
|
||||
if err != nil {
|
||||
if l.MFile != nil {
|
||||
_ = l.MFile.Close()
|
||||
}
|
||||
if l.RangeReadCloser != nil {
|
||||
_ = l.RangeReadCloser.Close()
|
||||
}
|
||||
for _, s := range ret {
|
||||
_ = s.Close()
|
||||
}
|
||||
return nil, nil, nil, errors.WithMessagef(err, "failed get [%s] stream", path)
|
||||
}
|
||||
ret = append(ret, ss)
|
||||
index++
|
||||
}
|
||||
return obj, t, ret, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getArchiveMeta(ctx context.Context, storage driver.Driver, path string, args model.ArchiveMetaArgs) (model.Obj, *model.ArchiveMetaProvider, error) {
|
||||
@ -94,13 +150,17 @@ func getArchiveMeta(ctx context.Context, storage driver.Driver, path string, arg
|
||||
return obj, archiveMetaProvider, err
|
||||
}
|
||||
}
|
||||
obj, t, ss, err := getArchiveToolAndStream(ctx, storage, path, args.LinkArgs)
|
||||
obj, t, ss, err := GetArchiveToolAndStream(ctx, storage, path, args.LinkArgs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := ss.Close(); err != nil {
|
||||
log.Errorf("failed to close file streamer, %v", err)
|
||||
var e error
|
||||
for _, s := range ss {
|
||||
e = stderrors.Join(e, s.Close())
|
||||
}
|
||||
if e != nil {
|
||||
log.Errorf("failed to close file streamer, %v", e)
|
||||
}
|
||||
}()
|
||||
meta, err := t.GetMeta(ss, args.ArchiveArgs)
|
||||
@ -114,9 +174,9 @@ func getArchiveMeta(ctx context.Context, storage driver.Driver, path string, arg
|
||||
if !storage.Config().NoCache {
|
||||
Expiration := time.Minute * time.Duration(storage.GetStorage().CacheExpiration)
|
||||
archiveMetaProvider.Expiration = &Expiration
|
||||
} else if ss.Link.MFile == nil {
|
||||
} else if ss[0].Link.MFile == nil {
|
||||
// alias、crypt 驱动
|
||||
archiveMetaProvider.Expiration = ss.Link.Expiration
|
||||
archiveMetaProvider.Expiration = ss[0].Link.Expiration
|
||||
}
|
||||
return obj, archiveMetaProvider, err
|
||||
}
|
||||
@ -188,13 +248,17 @@ func _listArchive(ctx context.Context, storage driver.Driver, path string, args
|
||||
return obj, files, err
|
||||
}
|
||||
}
|
||||
obj, t, ss, err := getArchiveToolAndStream(ctx, storage, path, args.LinkArgs)
|
||||
obj, t, ss, err := GetArchiveToolAndStream(ctx, storage, path, args.LinkArgs)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := ss.Close(); err != nil {
|
||||
log.Errorf("failed to close file streamer, %v", err)
|
||||
var e error
|
||||
for _, s := range ss {
|
||||
e = stderrors.Join(e, s.Close())
|
||||
}
|
||||
if e != nil {
|
||||
log.Errorf("failed to close file streamer, %v", e)
|
||||
}
|
||||
}()
|
||||
files, err := t.List(ss, args.ArchiveInnerArgs)
|
||||
@ -378,8 +442,8 @@ func driverExtract(ctx context.Context, storage driver.Driver, path string, args
|
||||
}
|
||||
|
||||
type streamWithParent struct {
|
||||
rc io.ReadCloser
|
||||
parent *stream.SeekableStream
|
||||
rc io.ReadCloser
|
||||
parents []*stream.SeekableStream
|
||||
}
|
||||
|
||||
func (s *streamWithParent) Read(p []byte) (int, error) {
|
||||
@ -387,24 +451,31 @@ func (s *streamWithParent) Read(p []byte) (int, error) {
|
||||
}
|
||||
|
||||
func (s *streamWithParent) Close() error {
|
||||
err1 := s.rc.Close()
|
||||
err2 := s.parent.Close()
|
||||
return stderrors.Join(err1, err2)
|
||||
err := s.rc.Close()
|
||||
for _, ss := range s.parents {
|
||||
err = stderrors.Join(err, ss.Close())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func InternalExtract(ctx context.Context, storage driver.Driver, path string, args model.ArchiveInnerArgs) (io.ReadCloser, int64, error) {
|
||||
_, t, ss, err := getArchiveToolAndStream(ctx, storage, path, args.LinkArgs)
|
||||
_, t, ss, err := GetArchiveToolAndStream(ctx, storage, path, args.LinkArgs)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
rc, size, err := t.Extract(ss, args)
|
||||
if err != nil {
|
||||
if e := ss.Close(); e != nil {
|
||||
var e error
|
||||
for _, s := range ss {
|
||||
e = stderrors.Join(e, s.Close())
|
||||
}
|
||||
if e != nil {
|
||||
log.Errorf("failed to close file streamer, %v", e)
|
||||
err = stderrors.Join(err, e)
|
||||
}
|
||||
return nil, 0, err
|
||||
}
|
||||
return &streamWithParent{rc: rc, parent: ss}, size, nil
|
||||
return &streamWithParent{rc: rc, parents: ss}, size, nil
|
||||
}
|
||||
|
||||
func ArchiveDecompress(ctx context.Context, storage driver.Driver, srcPath, dstDirPath string, args model.ArchiveDecompressArgs, lazyCache ...bool) error {
|
||||
|
Reference in New Issue
Block a user