Skip to content

Commit 9a9aee9

Browse files
authored
feat(alias): support writing to non-ambiguous paths (#8216)
* feat(alias): support writing to non-ambiguous paths * feat(alias): support extract concurrency * fix(alias): extract url no pass query
1 parent 4fcc3a1 commit 9a9aee9

File tree

6 files changed

+285
-29
lines changed

6 files changed

+285
-29
lines changed

drivers/alias/driver.go

Lines changed: 172 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package alias
33
import (
44
"context"
55
"errors"
6+
stdpath "path"
67
"strings"
78

89
"github.com/alist-org/alist/v3/internal/driver"
@@ -126,8 +127,46 @@ func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
126127
return nil, errs.ObjectNotFound
127128
}
128129

130+
func (d *Alias) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
131+
if !d.Writable {
132+
return errs.PermissionDenied
133+
}
134+
reqPath, err := d.getReqPath(ctx, parentDir, true)
135+
if err == nil {
136+
return fs.MakeDir(ctx, stdpath.Join(*reqPath, dirName))
137+
}
138+
if errs.IsNotImplement(err) {
139+
return errors.New("same-name dirs cannot make sub-dir")
140+
}
141+
return err
142+
}
143+
144+
func (d *Alias) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
145+
if !d.Writable {
146+
return errs.PermissionDenied
147+
}
148+
srcPath, err := d.getReqPath(ctx, srcObj, false)
149+
if errs.IsNotImplement(err) {
150+
return errors.New("same-name files cannot be moved")
151+
}
152+
if err != nil {
153+
return err
154+
}
155+
dstPath, err := d.getReqPath(ctx, dstDir, true)
156+
if errs.IsNotImplement(err) {
157+
return errors.New("same-name dirs cannot be moved to")
158+
}
159+
if err != nil {
160+
return err
161+
}
162+
return fs.Move(ctx, *srcPath, *dstPath)
163+
}
164+
129165
func (d *Alias) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
130-
reqPath, err := d.getReqPath(ctx, srcObj)
166+
if !d.Writable {
167+
return errs.PermissionDenied
168+
}
169+
reqPath, err := d.getReqPath(ctx, srcObj, false)
131170
if err == nil {
132171
return fs.Rename(ctx, *reqPath, newName)
133172
}
@@ -137,8 +176,33 @@ func (d *Alias) Rename(ctx context.Context, srcObj model.Obj, newName string) er
137176
return err
138177
}
139178

179+
func (d *Alias) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
180+
if !d.Writable {
181+
return errs.PermissionDenied
182+
}
183+
srcPath, err := d.getReqPath(ctx, srcObj, false)
184+
if errs.IsNotImplement(err) {
185+
return errors.New("same-name files cannot be copied")
186+
}
187+
if err != nil {
188+
return err
189+
}
190+
dstPath, err := d.getReqPath(ctx, dstDir, true)
191+
if errs.IsNotImplement(err) {
192+
return errors.New("same-name dirs cannot be copied to")
193+
}
194+
if err != nil {
195+
return err
196+
}
197+
_, err = fs.Copy(ctx, *srcPath, *dstPath)
198+
return err
199+
}
200+
140201
func (d *Alias) Remove(ctx context.Context, obj model.Obj) error {
141-
reqPath, err := d.getReqPath(ctx, obj)
202+
if !d.Writable {
203+
return errs.PermissionDenied
204+
}
205+
reqPath, err := d.getReqPath(ctx, obj, false)
142206
if err == nil {
143207
return fs.Remove(ctx, *reqPath)
144208
}
@@ -148,4 +212,110 @@ func (d *Alias) Remove(ctx context.Context, obj model.Obj) error {
148212
return err
149213
}
150214

215+
func (d *Alias) Put(ctx context.Context, dstDir model.Obj, s model.FileStreamer, up driver.UpdateProgress) error {
216+
if !d.Writable {
217+
return errs.PermissionDenied
218+
}
219+
reqPath, err := d.getReqPath(ctx, dstDir, true)
220+
if err == nil {
221+
return fs.PutDirectly(ctx, *reqPath, s)
222+
}
223+
if errs.IsNotImplement(err) {
224+
return errors.New("same-name dirs cannot be Put")
225+
}
226+
return err
227+
}
228+
229+
func (d *Alias) PutURL(ctx context.Context, dstDir model.Obj, name, url string) error {
230+
if !d.Writable {
231+
return errs.PermissionDenied
232+
}
233+
reqPath, err := d.getReqPath(ctx, dstDir, true)
234+
if err == nil {
235+
return fs.PutURL(ctx, *reqPath, name, url)
236+
}
237+
if errs.IsNotImplement(err) {
238+
return errors.New("same-name files cannot offline download")
239+
}
240+
return err
241+
}
242+
243+
func (d *Alias) GetArchiveMeta(ctx context.Context, obj model.Obj, args model.ArchiveArgs) (model.ArchiveMeta, error) {
244+
root, sub := d.getRootAndPath(obj.GetPath())
245+
dsts, ok := d.pathMap[root]
246+
if !ok {
247+
return nil, errs.ObjectNotFound
248+
}
249+
for _, dst := range dsts {
250+
meta, err := d.getArchiveMeta(ctx, dst, sub, args)
251+
if err == nil {
252+
return meta, nil
253+
}
254+
}
255+
return nil, errs.NotImplement
256+
}
257+
258+
func (d *Alias) ListArchive(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) ([]model.Obj, error) {
259+
root, sub := d.getRootAndPath(obj.GetPath())
260+
dsts, ok := d.pathMap[root]
261+
if !ok {
262+
return nil, errs.ObjectNotFound
263+
}
264+
for _, dst := range dsts {
265+
l, err := d.listArchive(ctx, dst, sub, args)
266+
if err == nil {
267+
return l, nil
268+
}
269+
}
270+
return nil, errs.NotImplement
271+
}
272+
273+
func (d *Alias) Extract(ctx context.Context, obj model.Obj, args model.ArchiveInnerArgs) (*model.Link, error) {
274+
// alias的两个驱动,一个支持驱动提取,一个不支持,如何兼容?
275+
// 如果访问的是不支持驱动提取的驱动内的压缩文件,GetArchiveMeta就会返回errs.NotImplement,提取URL前缀就会是/ae,Extract就不会被调用
276+
// 如果访问的是支持驱动提取的驱动内的压缩文件,GetArchiveMeta就会返回有效值,提取URL前缀就会是/ad,Extract就会被调用
277+
root, sub := d.getRootAndPath(obj.GetPath())
278+
dsts, ok := d.pathMap[root]
279+
if !ok {
280+
return nil, errs.ObjectNotFound
281+
}
282+
for _, dst := range dsts {
283+
link, err := d.extract(ctx, dst, sub, args)
284+
if err == nil {
285+
if !args.Redirect && len(link.URL) > 0 {
286+
if d.DownloadConcurrency > 0 {
287+
link.Concurrency = d.DownloadConcurrency
288+
}
289+
if d.DownloadPartSize > 0 {
290+
link.PartSize = d.DownloadPartSize * utils.KB
291+
}
292+
}
293+
return link, nil
294+
}
295+
}
296+
return nil, errs.NotImplement
297+
}
298+
299+
func (d *Alias) ArchiveDecompress(ctx context.Context, srcObj, dstDir model.Obj, args model.ArchiveDecompressArgs) error {
300+
if !d.Writable {
301+
return errs.PermissionDenied
302+
}
303+
srcPath, err := d.getReqPath(ctx, srcObj, false)
304+
if errs.IsNotImplement(err) {
305+
return errors.New("same-name files cannot be decompressed")
306+
}
307+
if err != nil {
308+
return err
309+
}
310+
dstPath, err := d.getReqPath(ctx, dstDir, true)
311+
if errs.IsNotImplement(err) {
312+
return errors.New("same-name dirs cannot be decompressed to")
313+
}
314+
if err != nil {
315+
return err
316+
}
317+
_, err = fs.ArchiveDecompress(ctx, *srcPath, *dstPath, args)
318+
return err
319+
}
320+
151321
var _ driver.Driver = (*Alias)(nil)

drivers/alias/meta.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ type Addition struct {
1313
ProtectSameName bool `json:"protect_same_name" default:"true" required:"false" help:"Protects same-name files from Delete or Rename"`
1414
DownloadConcurrency int `json:"download_concurrency" default:"0" required:"false" type:"number" help:"Need to enable proxy"`
1515
DownloadPartSize int `json:"download_part_size" default:"0" type:"number" required:"false" help:"Need to enable proxy. Unit: KB"`
16+
Writable bool `json:"writable" type:"bool" default:"false"`
1617
}
1718

1819
var config = driver.Config{
1920
Name: "Alias",
2021
LocalSort: true,
2122
NoCache: true,
22-
NoUpload: true,
23+
NoUpload: false,
2324
DefaultRoot: "/",
2425
ProxyRangeOption: true,
2526
}

drivers/alias/util.go

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package alias
33
import (
44
"context"
55
"fmt"
6+
"net/url"
67
stdpath "path"
78
"strings"
89

10+
"github.com/alist-org/alist/v3/internal/driver"
911
"github.com/alist-org/alist/v3/internal/errs"
1012
"github.com/alist-org/alist/v3/internal/fs"
1113
"github.com/alist-org/alist/v3/internal/model"
@@ -125,9 +127,9 @@ func (d *Alias) link(ctx context.Context, dst, sub string, args model.LinkArgs)
125127
return link, err
126128
}
127129

128-
func (d *Alias) getReqPath(ctx context.Context, obj model.Obj) (*string, error) {
130+
func (d *Alias) getReqPath(ctx context.Context, obj model.Obj, isParent bool) (*string, error) {
129131
root, sub := d.getRootAndPath(obj.GetPath())
130-
if sub == "" {
132+
if sub == "" && !isParent {
131133
return nil, errs.NotSupport
132134
}
133135
dsts, ok := d.pathMap[root]
@@ -156,3 +158,68 @@ func (d *Alias) getReqPath(ctx context.Context, obj model.Obj) (*string, error)
156158
}
157159
return reqPath, nil
158160
}
161+
162+
func (d *Alias) getArchiveMeta(ctx context.Context, dst, sub string, args model.ArchiveArgs) (model.ArchiveMeta, error) {
163+
reqPath := stdpath.Join(dst, sub)
164+
storage, reqActualPath, err := op.GetStorageAndActualPath(reqPath)
165+
if err != nil {
166+
return nil, err
167+
}
168+
if _, ok := storage.(driver.ArchiveReader); ok {
169+
return op.GetArchiveMeta(ctx, storage, reqActualPath, model.ArchiveMetaArgs{
170+
ArchiveArgs: args,
171+
Refresh: true,
172+
})
173+
}
174+
return nil, errs.NotImplement
175+
}
176+
177+
func (d *Alias) listArchive(ctx context.Context, dst, sub string, args model.ArchiveInnerArgs) ([]model.Obj, error) {
178+
reqPath := stdpath.Join(dst, sub)
179+
storage, reqActualPath, err := op.GetStorageAndActualPath(reqPath)
180+
if err != nil {
181+
return nil, err
182+
}
183+
if _, ok := storage.(driver.ArchiveReader); ok {
184+
return op.ListArchive(ctx, storage, reqActualPath, model.ArchiveListArgs{
185+
ArchiveInnerArgs: args,
186+
Refresh: true,
187+
})
188+
}
189+
return nil, errs.NotImplement
190+
}
191+
192+
func (d *Alias) extract(ctx context.Context, dst, sub string, args model.ArchiveInnerArgs) (*model.Link, error) {
193+
reqPath := stdpath.Join(dst, sub)
194+
storage, reqActualPath, err := op.GetStorageAndActualPath(reqPath)
195+
if err != nil {
196+
return nil, err
197+
}
198+
if _, ok := storage.(driver.ArchiveReader); ok {
199+
if _, ok := storage.(*Alias); !ok && !args.Redirect {
200+
link, _, err := op.DriverExtract(ctx, storage, reqActualPath, args)
201+
return link, err
202+
}
203+
_, err = fs.Get(ctx, reqPath, &fs.GetArgs{NoLog: true})
204+
if err != nil {
205+
return nil, err
206+
}
207+
if common.ShouldProxy(storage, stdpath.Base(sub)) {
208+
link := &model.Link{
209+
URL: fmt.Sprintf("%s/ap%s?inner=%s&pass=%s&sign=%s",
210+
common.GetApiUrl(args.HttpReq),
211+
utils.EncodePath(reqPath, true),
212+
utils.EncodePath(args.InnerPath, true),
213+
url.QueryEscape(args.Password),
214+
sign.SignArchive(reqPath)),
215+
}
216+
if args.HttpReq != nil && d.ProxyRange {
217+
link.RangeReadCloser = common.NoProxyRange
218+
}
219+
return link, nil
220+
}
221+
link, _, err := op.DriverExtract(ctx, storage, reqActualPath, args)
222+
return link, err
223+
}
224+
return nil, errs.NotImplement
225+
}

internal/fs/fs.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ package fs
22

33
import (
44
"context"
5+
log "github.com/sirupsen/logrus"
6+
"io"
7+
58
"github.com/alist-org/alist/v3/internal/driver"
9+
"github.com/alist-org/alist/v3/internal/errs"
610
"github.com/alist-org/alist/v3/internal/model"
711
"github.com/alist-org/alist/v3/internal/op"
812
"github.com/alist-org/alist/v3/internal/task"
9-
log "github.com/sirupsen/logrus"
10-
"io"
13+
"github.com/pkg/errors"
1114
)
1215

1316
// the param named path of functions in this package is a mount path
@@ -168,3 +171,19 @@ func Other(ctx context.Context, args model.FsOtherArgs) (interface{}, error) {
168171
}
169172
return res, err
170173
}
174+
175+
func PutURL(ctx context.Context, path, dstName, urlStr string) error {
176+
storage, dstDirActualPath, err := op.GetStorageAndActualPath(path)
177+
if err != nil {
178+
return errors.WithMessage(err, "failed get storage")
179+
}
180+
if storage.Config().NoUpload {
181+
return errors.WithStack(errs.UploadNotSupported)
182+
}
183+
_, ok := storage.(driver.PutURL)
184+
_, okResult := storage.(driver.PutURLResult)
185+
if !ok && !okResult {
186+
return errs.NotImplement
187+
}
188+
return op.PutURL(ctx, storage, dstDirActualPath, dstName, urlStr)
189+
}

0 commit comments

Comments
 (0)