@@ -4,12 +4,14 @@ import (
4
4
"bytes"
5
5
"context"
6
6
"encoding/base64"
7
+ "encoding/json"
7
8
"errors"
8
9
"fmt"
9
10
"io"
10
11
"net/http"
11
12
"strconv"
12
13
"strings"
14
+ "time"
13
15
14
16
"github.com/alist-org/alist/v3/drivers/base"
15
17
"github.com/alist-org/alist/v3/internal/conf"
@@ -19,7 +21,6 @@ import (
19
21
"github.com/alist-org/alist/v3/pkg/cookie"
20
22
"github.com/alist-org/alist/v3/pkg/utils"
21
23
"github.com/go-resty/resty/v2"
22
- json "github.com/json-iterator/go"
23
24
jsoniter "github.com/json-iterator/go"
24
25
)
25
26
@@ -35,6 +36,9 @@ func (d *Cloudreve) getUA() string {
35
36
}
36
37
37
38
func (d * Cloudreve ) request (method string , path string , callback base.ReqCallback , out interface {}) error {
39
+ if d .ref != nil {
40
+ return d .ref .request (method , path , callback , out )
41
+ }
38
42
u := d .Address + "/api/v3" + path
39
43
req := base .RestyClient .R ()
40
44
req .SetHeaders (map [string ]string {
@@ -79,11 +83,11 @@ func (d *Cloudreve) request(method string, path string, callback base.ReqCallbac
79
83
}
80
84
if out != nil && r .Data != nil {
81
85
var marshal []byte
82
- marshal , err = json .Marshal (r .Data )
86
+ marshal , err = jsoniter .Marshal (r .Data )
83
87
if err != nil {
84
88
return err
85
89
}
86
- err = json .Unmarshal (marshal , out )
90
+ err = jsoniter .Unmarshal (marshal , out )
87
91
if err != nil {
88
92
return err
89
93
}
@@ -187,12 +191,9 @@ func (d *Cloudreve) upLocal(ctx context.Context, stream model.FileStreamer, u Up
187
191
if utils .IsCanceled (ctx ) {
188
192
return ctx .Err ()
189
193
}
190
- utils .Log .Debugf ("[Cloudreve-Local] upload: %d" , finish )
191
- var byteSize = DEFAULT
192
194
left := stream .GetSize () - finish
193
- if left < DEFAULT {
194
- byteSize = left
195
- }
195
+ byteSize := min (left , DEFAULT )
196
+ utils .Log .Debugf ("[Cloudreve-Local] upload range: %d-%d/%d" , finish , finish + byteSize - 1 , stream .GetSize ())
196
197
byteData := make ([]byte , byteSize )
197
198
n , err := io .ReadFull (stream , byteData )
198
199
utils .Log .Debug (err , n )
@@ -205,9 +206,26 @@ func (d *Cloudreve) upLocal(ctx context.Context, stream model.FileStreamer, u Up
205
206
req .SetHeader ("Content-Length" , strconv .FormatInt (byteSize , 10 ))
206
207
req .SetHeader ("User-Agent" , d .getUA ())
207
208
req .SetBody (driver .NewLimitedUploadStream (ctx , bytes .NewReader (byteData )))
209
+ req .AddRetryCondition (func (r * resty.Response , err error ) bool {
210
+ if err != nil {
211
+ return true
212
+ }
213
+ if r .IsError () {
214
+ return true
215
+ }
216
+ var retryResp Resp
217
+ jErr := base .RestyClient .JSONUnmarshal (r .Body (), & retryResp )
218
+ if jErr != nil {
219
+ return true
220
+ }
221
+ if retryResp .Code != 0 {
222
+ return true
223
+ }
224
+ return false
225
+ })
208
226
}, nil )
209
227
if err != nil {
210
- break
228
+ return err
211
229
}
212
230
finish += byteSize
213
231
up (float64 (finish ) * 100 / float64 (stream .GetSize ()))
@@ -222,16 +240,15 @@ func (d *Cloudreve) upRemote(ctx context.Context, stream model.FileStreamer, u U
222
240
var finish int64 = 0
223
241
var chunk int = 0
224
242
DEFAULT := int64 (u .ChunkSize )
243
+ retryCount := 0
244
+ maxRetries := 3
225
245
for finish < stream .GetSize () {
226
246
if utils .IsCanceled (ctx ) {
227
247
return ctx .Err ()
228
248
}
229
- utils .Log .Debugf ("[Cloudreve-Remote] upload: %d" , finish )
230
- var byteSize = DEFAULT
231
249
left := stream .GetSize () - finish
232
- if left < DEFAULT {
233
- byteSize = left
234
- }
250
+ byteSize := min (left , DEFAULT )
251
+ utils .Log .Debugf ("[Cloudreve-Remote] upload range: %d-%d/%d" , finish , finish + byteSize - 1 , stream .GetSize ())
235
252
byteData := make ([]byte , byteSize )
236
253
n , err := io .ReadFull (stream , byteData )
237
254
utils .Log .Debug (err , n )
@@ -248,14 +265,43 @@ func (d *Cloudreve) upRemote(ctx context.Context, stream model.FileStreamer, u U
248
265
// req.Header.Set("Content-Length", strconv.Itoa(int(byteSize)))
249
266
req .Header .Set ("Authorization" , fmt .Sprint (credential ))
250
267
req .Header .Set ("User-Agent" , d .getUA ())
251
- finish += byteSize
252
- res , err := base .HttpClient .Do (req )
253
- if err != nil {
254
- return err
268
+ err = func () error {
269
+ res , err := base .HttpClient .Do (req )
270
+ if err != nil {
271
+ return err
272
+ }
273
+ defer res .Body .Close ()
274
+ if res .StatusCode != 200 {
275
+ return errors .New (res .Status )
276
+ }
277
+ body , err := io .ReadAll (res .Body )
278
+ if err != nil {
279
+ return err
280
+ }
281<
CEB7
/code>
+ var up Resp
282
+ err = json .Unmarshal (body , & up )
283
+ if err != nil {
284
+ return err
285
+ }
286
+ if up .Code != 0 {
287
+ return errors .New (up .Msg )
288
+ }
289
+ return nil
290
+ }()
291
+ if err == nil {
292
+ retryCount = 0
293
+ finish += byteSize
294
+ up (float64 (finish ) * 100 / float64 (stream .GetSize ()))
295
+ chunk ++
296
+ } else {
297
+ retryCount ++
298
+ if retryCount > maxRetries {
299
+ return fmt .Errorf ("upload failed after %d retries due to server errors, error: %s" , maxRetries , err )
300
+ }
301
+ backoff := time .Duration (1 << retryCount ) * time .Second
302
+ utils .Log .Warnf ("[Cloudreve-Remote] server errors while uploading, retrying after %v..." , backoff )
303
+ time .Sleep (backoff )
255
304
}
256
- _ = res .Body .Close ()
257
- up (float64 (finish ) * 100 / float64 (stream .GetSize ()))
258
- chunk ++
259
305
}
260
306
return nil
261
307
}
@@ -264,16 +310,15 @@ func (d *Cloudreve) upOneDrive(ctx context.Context, stream model.FileStreamer, u
264
310
uploadUrl := u .UploadURLs [0 ]
265
311
var finish int64 = 0
266
312
DEFAULT := int64 (u .ChunkSize )
313
+ retryCount := 0
314
+ maxRetries := 3
267
315
for finish < stream .GetSize () {
268
316
if utils .IsCanceled (ctx ) {
269
317
return ctx .Err ()
270
318
}
271
- utils .Log .Debugf ("[Cloudreve-OneDrive] upload: %d" , finish )
272
- var byteSize = DEFAULT
273
319
left := stream .GetSize () - finish
274
- if left < DEFAULT {
275
- byteSize = left
276
- }
320
+ byteSize := min (left , DEFAULT )
321
+ utils .Log .Debugf ("[Cloudreve-OneDrive] upload range: %d-%d/%d" , finish , finish + byteSize - 1 , stream .GetSize ())
277
322
byteData := make ([]byte , byteSize )
278
323
n , err := io .ReadFull (stream , byteData )
279
324
utils .Log .Debug (err , n )
@@ -295,39 +340,47 @@ func (d *Cloudreve) upOneDrive(ctx context.Context, stream model.FileStreamer, u
295
340
return err
296
341
}
297
342
// https://learn.microsoft.com/zh-cn/onedrive/developer/rest-api/api/driveitem_createuploadsession
298
- if res .StatusCode != 201 && res .StatusCode != 202 && res .StatusCode != 200 {
343
+ switch {
344
+ case res .StatusCode >= 500 && res .StatusCode <= 504 :
345
+ retryCount ++
346
+ if retryCount > maxRetries {
347
+ res .Body .Close ()
348
+ return fmt .Errorf ("upload failed after %d retries due to server errors, error %d" , maxRetries , res .StatusCode )
349
+ }
350
+ backoff := time .Duration (1 << retryCount ) * time .Second
351
+ utils .Log .Warnf ("[Cloudreve-OneDrive] server errors %d while uploading, retrying after %v..." , res .StatusCode , backoff )
352
+ time .Sleep (backoff )
353
+ case res .StatusCode != 201 && res .StatusCode != 202 && res .StatusCode != 200 :
299
354
data , _ := io .ReadAll (res .Body )
300
- _ = res .Body .Close ()
355
+ res .Body .Close ()
301
356
return errors .New (string (data ))
357
+ default :
358
+ res .Body .Close ()
359
+ retryCount = 0
360
+ finish += byteSize
361
+ up (float64 (finish ) * 100 / float64 (stream .GetSize ()))
302
362
}
303
- _ = res .Body .Close ()
304
- up (float64 (finish ) * 100 / float64 (stream .GetSize ()))
305
363
}
306
364
// 上传成功发送回调请求
307
- err := d .request (http .MethodPost , "/callback/onedrive/finish/" + u .SessionID , func (req * resty.Request ) {
365
+ return d .request (http .MethodPost , "/callback/onedrive/finish/" + u .SessionID , func (req * resty.Request ) {
308
366
req .SetBody ("{}" )
309
367
}, nil )
310
- if err != nil {
311
- return err
312
- }
313
- return nil
314
368
}
315
369
316
370
func (d * Cloudreve ) upS3 (ctx context.Context , stream model.FileStreamer , u UploadInfo , up driver.UpdateProgress ) error {
317
371
var finish int64 = 0
318
372
var chunk int = 0
319
373
var etags []string
320
374
DEFAULT := int64 (u .ChunkSize )
375
+ retryCount := 0
376
+ maxRetries := 3
321
377
for finish < stream .GetSize () {
322
378
if utils .IsCanceled (ctx ) {
323
379
return ctx .Err ()
324
380
}
325
- utils .Log .Debugf ("[Cloudreve-S3] upload: %d" , finish )
326
- var byteSize = DEFAULT
327
381
left := stream .GetSize () - finish
328
- if left < DEFAULT {
329
- byteSize = left
330
- }
382
+ byteSize := min (left , DEFAULT )
383
+ utils .Log .Debugf ("[Cloudreve-S3] upload range: %d-%d/%d" , finish , finish + byteSize - 1 , stream .GetSize ())
331
384
byteData := make ([]byte , byteSize )
332
385
n , err := io .ReadFull (stream , byteData )
333
386
utils .Log .Debug (err , n )
@@ -346,10 +399,26 @@ func (d *Cloudreve) upS3(ctx context.Context, stream model.FileStreamer, u Uploa
346
399
if err != nil {
347
400
return err
348
401
}
349
- _ = res .Body .Close ()
350
- etags = append (etags , res .Header .Get ("ETag" ))
351
- up (float64 (finish ) * 100 / float64 (stream .GetSize ()))
352
- chunk ++
402
+ etag := res .Header .Get ("ETag" )
403
+ res .Body .Close ()
404
+ switch {
405
+ case res .StatusCode != 200 :
406
+ retryCount ++
407
+ if retryCount > maxRetries {
408
+ return fmt .Errorf ("upload failed after %d retries due to server errors, error %d" , maxRetries , res .StatusCode )
409
+ }
410
+ backoff := time .Duration (1 << retryCount ) * time .Second
411
+ utils .Log .Warnf ("[Cloudreve-S3] server errors %d while uploading, retrying after %v..." , res .StatusCode , backoff )
412
+ time .Sleep (backoff )
413
+ case
754D
etag == "" :
414
+ return errors .New ("faild to get ETag from header" )
415
+ default :
416
+ retryCount = 0
417
+ etags = append (etags , etag )
418
+ finish += byteSize
419
+ up (float64 (finish ) * 100 / float64 (stream .GetSize ()))
420
+ chunk ++
421
+ }
353
422
}
354
423
355
424
// s3LikeFinishUpload
0 commit comments