Skip to content

Commit 1335f80

Browse files
authored
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
1 parent 704d385 commit 1335f80

File tree

19 files changed

+1040
-318
lines changed

19 files changed

+1040
-318
lines changed

go.mod

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ require (
8585
github.com/blevesearch/go-faiss v1.0.20 // indirect
8686
github.com/blevesearch/zapx/v16 v16.1.5 // indirect
8787
github.com/bodgit/plumbing v1.3.0 // indirect
88-
github.com/bodgit/sevenzip v1.6.0 // indirect
88+
github.com/bodgit/sevenzip v1.6.0
8989
github.com/bodgit/windows v1.0.1 // indirect
9090
github.com/bytedance/sonic/loader v0.1.1 // indirect
9191
github.com/charmbracelet/x/ansi v0.2.3 // indirect
@@ -106,14 +106,14 @@ require (
106106
github.com/kr/text v0.2.0 // indirect
107107
github.com/matoous/go-nanoid/v2 v2.1.0 // indirect
108108
github.com/microcosm-cc/bluemonday v1.0.27
109-
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 // indirect
109+
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78
110110
github.com/sorairolake/lzip-go v0.3.5 // indirect
111111
github.com/taruti/bytepool v0.0.0-20160310082835-5e3a9ea56543 // indirect
112112
github.com/therootcompany/xz v1.0.1 // indirect
113113
github.com/ulikunitz/xz v0.5.12 // indirect
114114
github.com/xhofe/115-sdk-go v0.1.4
115115
github.com/yuin/goldmark v1.7.8
116-
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
116+
go4.org v0.0.0-20230225012048-214862532bf5
117117
resty.dev/v3 v3.0.0-beta.2 // indirect
118118
)
119119

internal/archive/all.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ package archive
33
import (
44
_ "github.com/alist-org/alist/v3/internal/archive/archives"
55
_ "github.com/alist-org/alist/v3/internal/archive/iso9660"
6+
_ "github.com/alist-org/alist/v3/internal/archive/rardecode"
7+
_ "github.com/alist-org/alist/v3/internal/archive/sevenzip"
68
_ "github.com/alist-org/alist/v3/internal/archive/zip"
79
)

internal/archive/archives/archives.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,18 @@ import (
1616
type Archives struct {
1717
}
1818

19-
func (*Archives) AcceptedExtensions() []string {
19+
func (Archives) AcceptedExtensions() []string {
2020
return []string{
21-
".br", ".bz2", ".gz", ".lz4", ".lz", ".sz", ".s2", ".xz", ".zz", ".zst", ".tar", ".rar", ".7z",
21+
".br", ".bz2", ".gz", ".lz4", ".lz", ".sz", ".s2", ".xz", ".zz", ".zst", ".tar",
2222
}
2323
}
2424

25-
func (*Archives) GetMeta(ss *stream.SeekableStream, args model.ArchiveArgs) (model.ArchiveMeta, error) {
26-
fsys, err := getFs(ss, args)
25+
func (Archives) AcceptedMultipartExtensions() map[string]tool.MultipartExtension {
26+
return map[string]tool.MultipartExtension{}
27+
}
28+
29+
func (Archives) GetMeta(ss []*stream.SeekableStream, args model.ArchiveArgs) (model.ArchiveMeta, error) {
30+
fsys, err := getFs(ss[0], args)
2731
if err != nil {
2832
return nil, err
2933
}
@@ -47,8 +51,8 @@ func (*Archives) GetMeta(ss *stream.SeekableStream, args model.ArchiveArgs) (mod
4751
}, nil
4852
}
4953

50-
func (*Archives) List(ss *stream.SeekableStream, args model.ArchiveInnerArgs) ([]model.Obj, error) {
51-
fsys, err := getFs(ss, args.ArchiveArgs)
54+
func (Archives) List(ss []*stream.SeekableStream, args model.ArchiveInnerArgs) ([]model.Obj, error) {
55+
fsys, err := getFs(ss[0], args.ArchiveArgs)
5256
if err != nil {
5357
return nil, err
5458
}
@@ -69,8 +73,8 @@ func (*Archives) List(ss *stream.SeekableStream, args model.ArchiveInnerArgs) ([
6973
})
7074
}
7175

72-
func (*Archives) Extract(ss *stream.SeekableStream, args model.ArchiveInnerArgs) (io.ReadCloser, int64, error) {
73-
fsys, err := getFs(ss, args.ArchiveArgs)
76+
func (Archives) Extract(ss []*stream.SeekableStream, args model.ArchiveInnerArgs) (io.ReadCloser, int64, error) {
77+
fsys, err := getFs(ss[0], args.ArchiveArgs)
7478
if err != nil {
7579
return nil, 0, err
7680
}
@@ -85,8 +89,8 @@ func (*Archives) Extract(ss *stream.SeekableStream, args model.ArchiveInnerArgs)
8589
return file, stat.Size(), nil
8690
}
8791

88-
func (*Archives) Decompress(ss *stream.SeekableStream, outputPath string, args model.ArchiveInnerArgs, up model.UpdateProgress) error {
89-
fsys, err := getFs(ss, args.ArchiveArgs)
92+
func (Archives) Decompress(ss []*stream.SeekableStream, outputPath string, args model.ArchiveInnerArgs, up model.UpdateProgress) error {
93+
fsys, err := getFs(ss[0], args.ArchiveArgs)
9094
if err != nil {
9195
return err
9296
}
@@ -133,5 +137,5 @@ func (*Archives) Decompress(ss *stream.SeekableStream, outputPath string, args m
133137
var _ tool.Tool = (*Archives)(nil)
134138

135139
func init() {
136-
tool.RegisterTool(&Archives{})
140+
tool.RegisterTool(Archives{})
137141
}

internal/archive/iso9660/iso9660.go

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,23 @@ import (
1414
type ISO9660 struct {
1515
}
1616

17-
func (t *ISO9660) AcceptedExtensions() []string {
17+
func (ISO9660) AcceptedExtensions() []string {
1818
return []string{".iso"}
1919
}
2020

21-
func (t *ISO9660) GetMeta(ss *stream.SeekableStream, args model.ArchiveArgs) (model.ArchiveMeta, error) {
21+
func (ISO9660) AcceptedMultipartExtensions() map[string]tool.MultipartExtension {
22+
return map[string]tool.MultipartExtension{}
23+
}
24+
25+
func (ISO9660) GetMeta(ss []*stream.SeekableStream, args model.ArchiveArgs) (model.ArchiveMeta, error) {
2226
return &model.ArchiveMetaInfo{
2327
Comment: "",
2428
Encrypted: false,
2529
}, nil
2630
}
2731

28-
func (t *ISO9660) List(ss *stream.SeekableStream, args model.ArchiveInnerArgs) ([]model.Obj, error) {
29-
img, err := getImage(ss)
32+
func (ISO9660) List(ss []*stream.SeekableStream, args model.ArchiveInnerArgs) ([]model.Obj, error) {
33+
img, err := getImage(ss[0])
3034
if err != nil {
3135
return nil, err
3236
}
@@ -48,8 +52,8 @@ func (t *ISO9660) List(ss *stream.SeekableStream, args model.ArchiveInnerArgs) (
4852
return ret, nil
4953
}
5054

51-
func (t *ISO9660) Extract(ss *stream.SeekableStream, args model.ArchiveInnerArgs) (io.ReadCloser, int64, error) {
52-
img, err := getImage(ss)
55+
func (ISO9660) Extract(ss []*stream.SeekableStream, args model.ArchiveInnerArgs) (io.ReadCloser, int64, error) {
56+
img, err := getImage(ss[0])
5357
if err != nil {
5458
return nil, 0, err
5559
}
@@ -63,8 +67,8 @@ func (t *ISO9660) Extract(ss *stream.SeekableStream, args model.ArchiveInnerArgs
6367
return io.NopCloser(obj.Reader()), obj.Size(), nil
6468
}
6569

66-
func (t *ISO9660) Decompress(ss *stream.SeekableStream, outputPath string, args model.ArchiveInnerArgs, up model.UpdateProgress) error {
67-
img, err := getImage(ss)
70+
func (ISO9660) Decompress(ss []*stream.SeekableStream, outputPath string, args model.ArchiveInnerArgs, up model.UpdateProgress) error {
71+
img, err := getImage(ss[0])
6872
if err != nil {
6973
return err
7074
}
@@ -92,5 +96,5 @@ func (t *ISO9660) Decompress(ss *stream.SeekableStream, outputPath string, args
9296
var _ tool.Tool = (*ISO9660)(nil)
9397

9498
func init() {
95-
tool.RegisterTool(&ISO9660{})
99+
tool.RegisterTool(ISO9660{})
96100
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package rardecode
2+
3+
import (
4+
"github.com/alist-org/alist/v3/internal/archive/tool"
5+
"github.com/alist-org/alist/v3/internal/errs"
6+
"github.com/alist-org/alist/v3/internal/model"
7+
"github.com/alist-org/alist/v3/internal/stream"
8+
"github.com/nwaples/rardecode/v2"
9+
"io"
10+
"os"
11+
stdpath "path"
12+
"strings"
13+
)
14+
15+
type RarDecoder struct{}
16+
17+
func (RarDecoder) AcceptedExtensions() []string {
18+
return []string{".rar"}
19+
}
20+
21+
func (RarDecoder) AcceptedMultipartExtensions() map[string]tool.MultipartExtension {
22+
return map[string]tool.MultipartExtension{
23+
".part1.rar": {".part%d.rar", 2},
24+
}
25+
}
26+
27+
func (RarDecoder) GetMeta(ss []*stream.SeekableStream, args model.ArchiveArgs) (model.ArchiveMeta, error) {
28+
l, err := list(ss, args.Password)
29+
if err != nil {
30+
return nil, err
31+
}
32+
_, tree := tool.GenerateMetaTreeFromFolderTraversal(l)
33+
return &model.ArchiveMetaInfo{
34+
Comment: "",
35+
Encrypted: false,
36+
Tree: tree,
37+
}, nil
38+
}
39+
40+
func (RarDecoder) List(ss []*stream.SeekableStream, args model.ArchiveInnerArgs) ([]model.Obj, error) {
41+
return nil, errs.NotSupport
42+
}
43+
44+
func (RarDecoder) Extract(ss []*stream.SeekableStream, args model.ArchiveInnerArgs) (io.ReadCloser, int64, error) {
45+
reader, err := getReader(ss, args.Password)
46+
if err != nil {
47+
return nil, 0, err
48+
}
49+
innerPath := strings.TrimPrefix(args.InnerPath, "/")
50+
for {
51+
var header *rardecode.FileHeader
52+
header, err = reader.Next()
53+
if err == io.EOF {
54+
break
55+
}
56+
if err != nil {
57+
return nil, 0, err
58+
}
59+
if header.Name == innerPath {
60+
if header.IsDir {
61+
break
62+
}
63+
return io.NopCloser(reader), header.UnPackedSize, nil
64+
}
65+
}
66+
return nil, 0, errs.ObjectNotFound
67+
}
68+
69+
func (RarDecoder) Decompress(ss []*stream.SeekableStream, outputPath string, args model.ArchiveInnerArgs, up model.UpdateProgress) error {
70+
reader, err := getReader(ss, args.Password)
71+
if err != nil {
72+
return err
73+
}
74+
if args.InnerPath == "/" {
75+
for {
76+
var header *rardecode.FileHeader
77+
header, err = reader.Next()
78+
if err == io.EOF {
79+
break
80+
}
81+
if err != nil {
82+
return err
83+
}
84+
name := header.Name
85+
if header.IsDir {
86+
name = name + "/"
87+
}
88+
err = decompress(reader, header, name, outputPath)
89+
if err != nil {
90+
return err
91+
}
92+
}
93+
} else {
94+
innerPath := strings.TrimPrefix(args.InnerPath, "/")
95+
innerBase := stdpath.Base(innerPath)
96+
createdBaseDir := false
97+
for {
98+
var header *rardecode.FileHeader
99+
header, err = reader.Next()
100+
if err == io.EOF {
101+
break
102+
}
103+
if err != nil {
104+
return err
105+
}
106+
name := header.Name
107+
if header.IsDir {
108+
name = name + "/"
109+
}
110+
if name == innerPath {
111+
err = _decompress(reader, header, outputPath, up)
112+
if err != nil {
113+
return err
114+
}
115+
break
116+
} else if strings.HasPrefix(name, innerPath+"/") {
117+
targetPath := stdpath.Join(outputPath, innerBase)
118+
if !createdBaseDir {
119+
err = os.Mkdir(targetPath, 0700)
120+
if err != nil {
121+
return err
122+
}
123+
createdBaseDir = true
124+
}
125+
restPath := strings.TrimPrefix(name, innerPath+"/")
126+
err = decompress(reader, header, restPath, targetPath)
127+
if err != nil {
128+
return err
129+
}
130+
}
131+
}
132+
}
133+
return nil
134+
}
135+
136+
var _ tool.Tool = (*RarDecoder)(nil)
137+
138+
func init() {
139+
tool.RegisterTool(RarDecoder{})
140+
}

0 commit comments

Comments
 (0)