Skip to content

Commit f542187

Browse files
totza2010Copilot
andauthored
fix(drivers/teldrive): enhance file listing and upload functionality with pagination and random chunk naming (OpenListTeam#2034)
* fix(drivers/teldrive): enhance file listing and upload functionality with pagination and random chunk naming * fix(drivers/teldrive): optimize file listing by removing unnecessary mutex and restructuring data handling * Update drivers/teldrive/meta.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Chaloemchai <chaloemchai.yy@gmail.com> --------- Signed-off-by: Chaloemchai <chaloemchai.yy@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent e3c664f commit f542187

File tree

3 files changed

+96
-24
lines changed

3 files changed

+96
-24
lines changed

drivers/teldrive/driver.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"net/url"
99
"path"
10+
"strconv"
1011
"strings"
1112

1213
"github.com/OpenListTeam/OpenList/v4/drivers/base"
@@ -17,6 +18,7 @@ import (
1718
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
1819
"github.com/go-resty/resty/v2"
1920
"github.com/google/uuid"
21+
"golang.org/x/sync/errgroup"
2022
)
2123

2224
type Teldrive struct {
@@ -53,18 +55,58 @@ func (d *Teldrive) Drop(ctx context.Context) error {
5355
}
5456

5557
func (d *Teldrive) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
56-
var listResp ListResp
58+
var firstResp ListResp
5759
err := d.request(http.MethodGet, "/api/files", func(req *resty.Request) {
5860
req.SetQueryParams(map[string]string{
5961
"path": dir.GetPath(),
60-
"limit": "1000", // overide default 500, TODO pagination
62+
"limit": "500",
63+
"page": "1",
6164
})
62-
}, &listResp)
65+
}, &firstResp)
66+
6367
if err != nil {
6468
return nil, err
6569
}
6670

67-
return utils.SliceConvert(listResp.Items, func(src Object) (model.Obj, error) {
71+
pagesData := make([][]Object, firstResp.Meta.TotalPages)
72+
pagesData[0] = firstResp.Items
73+
74+
if firstResp.Meta.TotalPages > 1 {
75+
g, _ := errgroup.WithContext(ctx)
76+
g.SetLimit(8)
77+
78+
for i := 2; i <= firstResp.Meta.TotalPages; i++ {
79+
page := i
80+
g.Go(func() error {
81+
var resp ListResp
82+
err := d.request(http.MethodGet, "/api/files", func(req *resty.Request) {
83+
req.SetQueryParams(map[string]string{
84+
"path": dir.GetPath(),
85+
"limit": "500",
86+
"page": strconv.Itoa(page),
87+
})
88+
}, &resp)
89+
90+
if err != nil {
91+
return err
92+
}
93+
94+
pagesData[page-1] = resp.Items
95+
return nil
96+
})
97+
}
98+
99+
if err := g.Wait(); err != nil {
100+
return nil, err
101+
}
102+
}
103+
104+
var allItems []Object
105+
for _, items := range pagesData {
106+
allItems = append(allItems, items...)
107+
}
108+
109+
return utils.SliceConvert(allItems, func(src Object) (model.Obj, error) {
68110
return &model.Object{
69111
Path: path.Join(dir.GetPath(), src.Name),
70112
ID: src.ID,
@@ -184,7 +226,7 @@ func (d *Teldrive) Put(ctx context.Context, dstDir model.Obj, file model.FileStr
184226
}
185227

186228
if totalParts <= 1 {
187-
return d.doSingleUpload(ctx, dstDir, file, up, totalParts, chunkSize, fileId)
229+
return d.doSingleUpload(ctx, dstDir, file, up, maxRetried, totalParts, chunkSize, fileId)
188230
}
189231

190232
return d.doMultiUpload(ctx, dstDir, file, up, maxRetried, totalParts, chunkSize, fileId)

drivers/teldrive/meta.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ type Addition struct {
1111
Cookie string `json:"cookie" type:"string" required:"true" help:"access_token=xxx"`
1212
UseShareLink bool `json:"use_share_link" type:"bool" default:"false" help:"Create share link when getting link to support 302. If disabled, you need to enable web proxy."`
1313
ChunkSize int64 `json:"chunk_size" type:"number" default:"10" help:"Chunk size in MiB"`
14+
RandomChunkName bool `json:"random_chunk_name" type:"bool" default:"true" help:"Random chunk name"`
1415
UploadConcurrency int64 `json:"upload_concurrency" type:"number" default:"4" help:"Concurrency upload requests"`
1516
}
1617

drivers/teldrive/upload.go

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package teldrive
22

33
import (
4+
"crypto/md5"
5+
"encoding/hex"
46
"fmt"
57
"io"
68
"net/http"
@@ -16,6 +18,7 @@ import (
1618
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
1719
"github.com/avast/retry-go"
1820
"github.com/go-resty/resty/v2"
21+
"github.com/google/uuid"
1922
"github.com/pkg/errors"
2023
"golang.org/x/net/context"
2124
"golang.org/x/sync/errgroup"
@@ -38,6 +41,11 @@ func (d *Teldrive) touch(name, path string) error {
3841
return nil
3942
}
4043

44+
func getMD5Hash(text string) string {
45+
hash := md5.Sum([]byte(text))
46+
return hex.EncodeToString(hash[:])
47+
}
48+
4149
func (d *Teldrive) createFileOnUploadSuccess(name, id, path string, uploadedFileParts []FilePart, totalSize int64) error {
4250
remoteFileParts, err := d.getFilePart(id)
4351
if err != nil {
@@ -101,12 +109,10 @@ func (d *Teldrive) getFilePart(fileId string) ([]FilePart, error) {
101109
return uploadedParts, nil
102110
}
103111

104-
func (d *Teldrive) singleUploadRequest(fileId string, callback base.ReqCallback, resp interface{}) error {
112+
func (d *Teldrive) singleUploadRequest(ctx context.Context, fileId string, callback base.ReqCallback, resp any) error {
105113
url := d.Address + "/api/uploads/" + fileId
106114
client := resty.New().SetTimeout(0)
107115

108-
ctx := context.Background()
109-
110116
req := client.R().
111117
SetContext(ctx)
112118
req.SetHeader("Cookie", d.Cookie)
@@ -135,16 +141,18 @@ func (d *Teldrive) singleUploadRequest(fileId string, callback base.ReqCallback,
135141
}
136142

137143
func (d *Teldrive) doSingleUpload(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up model.UpdateProgress,
138-
totalParts int, chunkSize int64, fileId string) error {
144+
maxRetried, totalParts int, chunkSize int64, fileId string) error {
139145

140146
totalSize := file.GetSize()
141147
var fileParts []FilePart
142148
var uploaded int64 = 0
143-
ss, err := stream.NewStreamSectionReader(file, int(totalSize), &up)
149+
var partName string
150+
chunkSize = min(totalSize, chunkSize)
151+
ss, err := stream.NewStreamSectionReader(file, int(chunkSize), &up)
144152
if err != nil {
145153
return err
146154
}
147-
155+
chunkCnt := 0
148156
for uploaded < totalSize {
149157
if utils.IsCanceled(ctx) {
150158
return ctx.Err()
@@ -154,20 +162,27 @@ func (d *Teldrive) doSingleUpload(ctx context.Context, dstDir model.Obj, file mo
154162
if err != nil {
155163
return err
156164
}
165+
chunkCnt += 1
157166
filePart := &FilePart{}
158167
if err := retry.Do(func() error {
159168

160169
if _, err := rd.Seek(0, io.SeekStart); err != nil {
161170
return err
162171
}
163172

164-
if err := d.singleUploadRequest(fileId, func(req *resty.Request) {
173+
if d.RandomChunkName {
174+
partName = getMD5Hash(uuid.New().String())
175+
} else {
176+
partName = file.GetName()
177+
if totalParts > 1 {
178+
partName = fmt.Sprintf("%s.part.%03d", file.GetName(), chunkCnt)
179+
}
180+
}
181+
182+
if err := d.singleUploadRequest(ctx, fileId, func(req *resty.Request) {
165183
uploadParams := map[string]string{
166-
"partName": func() string {
167-
digits := len(strconv.Itoa(totalParts))
168-
return file.GetName() + fmt.Sprintf(".%0*d", digits, 1)
169-
}(),
170-
"partNo": strconv.Itoa(1),
184+
"partName": partName,
185+
"partNo": strconv.Itoa(chunkCnt),
171186
"fileName": file.GetName(),
172187
}
173188
req.SetQueryParams(uploadParams)
@@ -180,7 +195,7 @@ func (d *Teldrive) doSingleUpload(ctx context.Context, dstDir model.Obj, file mo
180195
return nil
181196
},
182197
retry.Context(ctx),
183-
retry.Attempts(3),
198+
retry.Attempts(uint(maxRetried)),
184199
retry.DelayType(retry.BackOffDelay),
185200
retry.Delay(time.Second)); err != nil {
186201
return err
@@ -189,8 +204,11 @@ func (d *Teldrive) doSingleUpload(ctx context.Context, dstDir model.Obj, file mo
189204
if filePart.Name != "" {
190205
fileParts = append(fileParts, *filePart)
191206
uploaded += curChunkSize
192-
up(float64(uploaded) / float64(totalSize))
207+
up(float64(uploaded) / float64(totalSize) * 100)
193208
ss.FreeSectionReader(rd)
209+
} else {
210+
// For common situation this code won't reach
211+
return fmt.Errorf("[Teldrive] upload chunk %d failed: filePart Somehow missing", chunkCnt)
194212
}
195213

196214
}
@@ -318,6 +336,7 @@ func (d *Teldrive) doMultiUpload(ctx context.Context, dstDir model.Obj, file mod
318336
func (d *Teldrive) uploadSingleChunk(ctx context.Context, fileId string, task chunkTask, totalParts, maxRetried int) (*FilePart, error) {
319337
filePart := &FilePart{}
320338
retryCount := 0
339+
var partName string
321340
defer task.ss.FreeSectionReader(task.reader)
322341

323342
for {
@@ -331,12 +350,22 @@ func (d *Teldrive) uploadSingleChunk(ctx context.Context, fileId string, task ch
331350
return &existingPart, nil
332351
}
333352

334-
err := d.singleUploadRequest(fileId, func(req *resty.Request) {
353+
if _, err := task.reader.Seek(0, io.SeekStart); err != nil {
354+
return nil, err
355+
}
356+
357+
if d.RandomChunkName {
358+
partName = getMD5Hash(uuid.New().String())
359+
} else {
360+
partName = task.fileName
361+
if totalParts > 1 {
362+
partName = fmt.Sprintf("%s.part.%03d", task.fileName, task.chunkIdx)
363+
}
364+
}
365+
366+
err := d.singleUploadRequest(ctx, fileId, func(req *resty.Request) {
335367
uploadParams := map[string]string{
336-
"partName": func() string {
337-
digits := len(strconv.Itoa(totalParts))
338-
return task.fileName + fmt.Sprintf(".%0*d", digits, task.chunkIdx)
339-
}(),
368+
"partName": partName,
340369
"partNo": strconv.Itoa(task.chunkIdx),
341370
"fileName": task.fileName,
342371
}

0 commit comments

Comments
 (0)