feat(archive): support multipart archives (#8184 close #8015)

* 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:
KirCute
2025-03-27 23:20:44 +08:00
committed by GitHub
parent 704d3854df
commit 1335f80362
19 changed files with 1042 additions and 320 deletions

View File

@ -0,0 +1,72 @@
package sevenzip
import (
"io"
"strings"
"github.com/alist-org/alist/v3/internal/archive/tool"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/stream"
)
type SevenZip struct{}
func (SevenZip) AcceptedExtensions() []string {
return []string{".7z"}
}
func (SevenZip) AcceptedMultipartExtensions() map[string]tool.MultipartExtension {
return map[string]tool.MultipartExtension{
".7z.001": {".7z.%.3d", 2},
}
}
func (SevenZip) GetMeta(ss []*stream.SeekableStream, args model.ArchiveArgs) (model.ArchiveMeta, error) {
reader, err := getReader(ss, args.Password)
if err != nil {
return nil, err
}
_, tree := tool.GenerateMetaTreeFromFolderTraversal(&WrapReader{Reader: reader})
return &model.ArchiveMetaInfo{
Comment: "",
Encrypted: args.Password != "",
Tree: tree,
}, nil
}
func (SevenZip) List(ss []*stream.SeekableStream, args model.ArchiveInnerArgs) ([]model.Obj, error) {
return nil, errs.NotSupport
}
func (SevenZip) Extract(ss []*stream.SeekableStream, args model.ArchiveInnerArgs) (io.ReadCloser, int64, error) {
reader, err := getReader(ss, args.Password)
if err != nil {
return nil, 0, err
}
innerPath := strings.TrimPrefix(args.InnerPath, "/")
for _, file := range reader.File {
if file.Name == innerPath {
r, e := file.Open()
if e != nil {
return nil, 0, e
}
return r, file.FileInfo().Size(), nil
}
}
return nil, 0, errs.ObjectNotFound
}
func (SevenZip) Decompress(ss []*stream.SeekableStream, outputPath string, args model.ArchiveInnerArgs, up model.UpdateProgress) error {
reader, err := getReader(ss, args.Password)
if err != nil {
return err
}
return tool.DecompressFromFolderTraversal(&WrapReader{Reader: reader}, outputPath, args, up)
}
var _ tool.Tool = (*SevenZip)(nil)
func init() {
tool.RegisterTool(SevenZip{})
}

View File

@ -0,0 +1,61 @@
package sevenzip
import (
"errors"
"github.com/alist-org/alist/v3/internal/archive/tool"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/stream"
"github.com/bodgit/sevenzip"
"io"
"io/fs"
)
type WrapReader struct {
Reader *sevenzip.Reader
}
func (r *WrapReader) Files() []tool.SubFile {
ret := make([]tool.SubFile, 0, len(r.Reader.File))
for _, f := range r.Reader.File {
ret = append(ret, &WrapFile{f: f})
}
return ret
}
type WrapFile struct {
f *sevenzip.File
}
func (f *WrapFile) Name() string {
return f.f.Name
}
func (f *WrapFile) FileInfo() fs.FileInfo {
return f.f.FileInfo()
}
func (f *WrapFile) Open() (io.ReadCloser, error) {
return f.f.Open()
}
func getReader(ss []*stream.SeekableStream, password string) (*sevenzip.Reader, error) {
readerAt, err := stream.NewMultiReaderAt(ss)
if err != nil {
return nil, err
}
sr, err := sevenzip.NewReaderWithPassword(readerAt, readerAt.Size(), password)
if err != nil {
return nil, filterPassword(err)
}
return sr, nil
}
func filterPassword(err error) error {
if err != nil {
var e *sevenzip.ReadError
if errors.As(err, &e) && e.Encrypted {
return errs.WrongArchivePassword
}
}
return err
}