@@ -17,9 +17,11 @@ import (
17
17
"github.com/OpenListTeam/OpenList/v4/internal/op"
18
18
"github.com/OpenListTeam/OpenList/v4/internal/sign"
19
19
"github.com/OpenListTeam/OpenList/v4/internal/stream"
20
+ "github.com/OpenListTeam/OpenList/v4/pkg/errgroup"
20
21
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
21
22
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
22
23
"github.com/OpenListTeam/OpenList/v4/server/common"
24
+ "github.com/avast/retry-go"
23
25
)
24
26
25
27
type Chunk struct {
@@ -39,6 +41,9 @@ func (d *Chunk) Init(ctx context.Context) error {
39
41
if d .PartSize <= 0 {
40
42
return errors .New ("part size must be positive" )
41
43
}
44
+ if len (d .ChunkPrefix ) <= 0 {
45
+ return errors .New ("chunk folder prefix must not be empty" )
46
+ }
42
47
d .RemotePath = utils .FixAndCleanPath (d .RemotePath )
43
48
return nil
44
49
}
@@ -72,13 +77,13 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) {
72
77
}
73
78
74
79
remoteActualDir , name := stdpath .Split (remoteActualPath )
75
- chunkName := "[openlist_chunk]" + name
80
+ chunkName := d . ChunkPrefix + name
76
81
chunkObjs , err := op .List (ctx , remoteStorage , stdpath .Join (remoteActualDir , chunkName ), model.ListArgs {})
77
82
if err != nil {
78
83
return nil , err
79
84
}
80
85
var totalSize int64 = 0
81
- // 0号块必须存在
86
+ // 0号块默认为-1 以支持空文件
82
87
chunkSizes := []int64 {- 1 }
83
88
h := make (map [* utils.HashType ]string )
84
89
var first model.Obj
@@ -115,21 +120,6 @@ func (d *Chunk) Get(ctx context.Context, path string) (model.Obj, error) {
115
120
chunkSizes [idx ] = o .GetSize ()
116
121
}
117
122
}
118
- // 检查0号块不等于-1 以支持空文件
119
- // 如果块数量大于1 最后一块不可能为0
120
- // 只检查中间块是否有0
121
- for i , l := 0 , len (chunkSizes )- 2 ; ; i ++ {
122
- if i == 0 {
123
- if chunkSizes [i ] == - 1 {
124
- return nil , fmt .Errorf ("chunk part[%d] are missing" , i )
125
- }
126
- } else if chunkSizes [i ] == 0 {
127
- return nil , fmt .Errorf ("chunk part[%d] are missing" , i )
128
- }
129
- if i >= l {
130
- break
131
- }
132
- }
133
123
reqDir , _ := stdpath .Split (path )
134
124
objRes := chunkObject {
135
125
Object : model.Object {
@@ -161,67 +151,76 @@ func (d *Chunk) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
161
151
return nil , err
162
152
}
163
153
result := make ([]model.Obj , 0 , len (remoteObjs ))
154
+ listG , listCtx := errgroup .NewGroupWithContext (ctx , d .NumListWorkers , retry .Attempts (3 ))
164
155
for _ , obj := range remoteObjs {
156
+ if utils .IsCanceled (listCtx ) {
157
+ break
158
+ }
165
159
rawName := obj .GetName ()
166
160
if obj .IsDir () {
167
- if name , ok := strings .CutPrefix (rawName , "[openlist_chunk]" ); ok {
168
- chunkObjs , err := op .List (ctx , remoteStorage , stdpath .Join (remoteActualDir , rawName ), model.ListArgs {
169
- ReqPath : stdpath .Join (args .ReqPath , rawName ),
170
- Refresh : args .Refresh ,
171
- })
172
- if err != nil {
173
- return nil , err
174
- }
175
- totalSize := int64 (0 )
176
- h := make (map [* utils.HashType ]string )
177
- first := obj
178
- for _ , o := range chunkObjs {
179
- if o .IsDir () {
180
- continue
161
+ if name , ok := strings .CutPrefix (rawName , d .ChunkPrefix ); ok {
162
+ resultIdx := len (result )
163
+ result = append (result , nil )
164
+ listG .Go (func (ctx context.Context ) error {
165
+ chunkObjs , err := op .List (ctx , remoteStorage , stdpath .Join (remoteActualDir , rawName ), model.ListArgs {
166
+ ReqPath : stdpath .Join (args .ReqPath , rawName ),
167
+ Refresh : args .Refresh ,
168
+ })
169
+ if err != nil {
170
+ return err
181
171
}
182
- if after , ok := strings .CutPrefix (strings .TrimSuffix (o .GetName (), d .CustomExt ), "hash_" ); ok {
183
- hn , value , ok := strings .Cut (after , "_" )
184
- if ok {
185
- ht , ok := utils .GetHashByName (hn )
172
+ totalSize := int64 (0 )
173
+ h := make (map [* utils.HashType ]string )
174
+ first := obj
175
+ for _ , o := range chunkObjs {
176
+ if o .IsDir () {
177
+ continue
178
+ }
179
+ if after , ok := strings .CutPrefix (strings .TrimSuffix (o .GetName (), d .CustomExt ), "hash_" ); ok {
180
+ hn , value , ok := strings .Cut (after , "_" )
186
181
if ok {
187
- h [ht ] = value
182
+ ht , ok := utils .GetHashByName (hn )
183
+ if ok {
184
+ h [ht ] = value
185
+ }
186
+ continue
188
187
}
188
+ }
189
+ idx , err := strconv .Atoi (strings .TrimSuffix (o .GetName (), d .CustomExt ))
190
+ if err != nil {
189
191
continue
190
192
}
193
+ if idx == 0 {
194
+ first = o
195
+ }
196
+ totalSize += o .GetSize ()
191
197
}
192
- idx , err := strconv .Atoi (strings .TrimSuffix (o .GetName (), d .CustomExt ))
193
- if err != nil {
194
- continue
198
+ objRes := model.Object {
199
+ Name : name ,
200
+ Size : totalSize ,
201
+ Modified : first .ModTime (),
202
+ Ctime : first .CreateTime (),
195
203
}
196
- if idx == 0 {
197
- first = o
204
+ if len ( h ) > 0 {
205
+ objRes . HashInfo = utils . NewHashInfoByMap ( h )
198
206
}
199
- totalSize += o .GetSize ()
200
- }
201
- objRes := model.Object {
202
- Name : name ,
203
- Size : totalSize ,
204
- Modified : first .ModTime (),
205
- Ctime : first .CreateTime (),
206
- }
207
- if len (h ) > 0 {
208
- objRes .HashInfo = utils .NewHashInfoByMap (h )
209
- }
210
- if ! d .Thumbnail {
211
- result = append (result , & objRes )
212
- } else {
213
- thumbPath := stdpath .Join (args .ReqPath , ".thumbnails" , name + ".webp" )
214
- thumb := fmt .Sprintf ("%s/d%s?sign=%s" ,
215
- common .GetApiUrl (ctx ),
216
- utils .EncodePath (thumbPath , true ),
217
- sign .Sign (thumbPath ))
218
- result = append (result , & model.ObjThumb {
219
- Object : objRes ,
220
- Thumbnail : model.Thumbnail {
221
- Thumbnail : thumb ,
222
- },
223
- })
224
- }
207
+ if ! d .Thumbnail {
208
+ result [resultIdx ] = & objRes
209
+ } else {
210
+ thumbPath := stdpath .Join (args .ReqPath , ".thumbnails" , name + ".webp" )
211
+ thumb := fmt .Sprintf ("%s/d%s?sign=%s" ,
212
+ common .GetApiUrl (ctx ),
213
+ utils .EncodePath (thumbPath , true ),
214
+ sign .Sign (thumbPath ))
215
+ result [resultIdx ] = & model.ObjThumb {
216
+ Object : objRes ,
217
+ Thumbnail : model.Thumbnail {
218
+ Thumbnail : thumb ,
219
+ },
220
+ }
221
+ }
222
+ return nil
223
+ })
225
224
continue
226
225
}
227
226
}
@@ -248,6 +247,9 @@ func (d *Chunk) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
248
247
})
249
248
}
250
249
}
250
+ if err = listG .Wait (); err != nil {
251
+ return nil , err
252
+ }
251
253
return result , nil
252
254
}
253
255
@@ -267,6 +269,21 @@ func (d *Chunk) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
267
269
resultLink .SyncClosers = utils .NewSyncClosers (l )
268
270
return & resultLink , nil
269
271
}
272
+ // 检查0号块不等于-1 以支持空文件
273
+ // 如果块数量大于1 最后一块不可能为0
274
+ // 只检查中间块是否有0
275
+ for i , l := 0 , len (chunkFile .chunkSizes )- 2 ; ; i ++ {
276
+ if i == 0 {
277
+ if chunkFile .chunkSizes [i ] == - 1 {
278
+ return nil , fmt .Errorf ("chunk part[%d] are missing" , i )
279
+ }
280
+ } else if chunkFile .chunkSizes [i ] == 0 {
281
+ return nil , fmt .Errorf ("chunk part[%d] are missing" , i )
282
+ }
283
+ if i >= l {
284
+ break
285
+ }
286
+ }
270
287
fileSize := chunkFile .GetSize ()
271
288
mergedRrf := func (ctx context.Context , httpRange http_range.Range ) (io.ReadCloser , error ) {
272
289
start := httpRange .Start
@@ -383,7 +400,7 @@ func (d *Chunk) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
383
400
384
401
func (d * Chunk ) Rename (ctx context.Context , srcObj model.Obj , newName string ) error {
385
402
if _ , ok := srcObj .(* chunkObject ); ok {
386
- newName = "[openlist_chunk]" + newName
403
+ newName = d . ChunkPrefix + newName
387
404
}
388
405
return fs .Rename (ctx , stdpath .Join (d .RemotePath , srcObj .GetPath ()), newName )
389
406
}
@@ -404,14 +421,14 @@ func (d *Chunk) Put(ctx context.Context, dstDir model.Obj, file model.FileStream
404
421
if err != nil {
405
422
return err
406
423
}
407
- if d .Thumbnail && dstDir .GetName () == ".thumbnails" {
424
+ if ( d .Thumbnail && dstDir .GetName () == ".thumbnails" ) || ( d . ChunkLargeFileOnly && file . GetSize () <= d . PartSize ) {
408
425
return op .Put (ctx , remoteStorage , stdpath .Join (remoteActualPath , dstDir .GetPath ()), file , up )
409
426
}
410
427
upReader := & driver.ReaderUpdatingProgress {
411
428
Reader : file ,
412
429
UpdateProgress : up ,
413
430
}
414
- dst := stdpath .Join (remoteActualPath , dstDir .GetPath (), "[openlist_chunk]" + file .GetName ())
431
+ dst := stdpath .Join (remoteActualPath , dstDir .GetPath (), d . ChunkPrefix + file .GetName ())
415
432
if d .StoreHash {
416
433
for ht , value := range file .GetHash ().All () {
417
434
_ = op .Put (ctx , remoteStorage , dst , & stream.FileStream {
0 commit comments