@@ -20,6 +20,7 @@ import (
2020 "github.com/OpenListTeam/OpenList/v4/drivers/base"
2121 "github.com/OpenListTeam/OpenList/v4/internal/model"
2222 "github.com/OpenListTeam/OpenList/v4/internal/op"
23+ "github.com/OpenListTeam/OpenList/v4/pkg/utils"
2324 "github.com/go-resty/resty/v2"
2425 log "github.com/sirupsen/logrus"
2526)
@@ -283,8 +284,15 @@ func (d *QuarkOpen) getProofRange(proofSeed string, fileSize int64) (*ProofRange
283284
284285func (d * QuarkOpen ) _getPartInfo (stream model.FileStreamer , partSize int64 ) []base.Json {
285286 // 计算分片信息
286- partInfo := make ([]base.Json , 0 )
287287 total := stream .GetSize ()
288+
289+ // 确保partSize合理:最小4MB,避免分片过多
290+ const minPartSize int64 = 4 * utils .MB
291+ if partSize < minPartSize {
292+ partSize = minPartSize
293+ }
294+
295+ partInfo := make ([]base.Json , 0 )
288296 left := total
289297 partNumber := 1
290298
@@ -304,6 +312,7 @@ func (d *QuarkOpen) _getPartInfo(stream model.FileStreamer, partSize int64) []ba
304312 partNumber ++
305313 }
306314
315+ log .Infof ("[quark_open] Upload plan: file_size=%d, part_size=%d, part_count=%d" , total , partSize , len (partInfo ))
307316 return partInfo
308317}
309318
@@ -340,13 +349,43 @@ func (d *QuarkOpen) upPart(ctx context.Context, upUrlInfo UpUrlInfo, partNumber
340349 req .Header .Set ("Accept-Encoding" , "gzip" )
341350 req .Header .Set ("User-Agent" , "Go-http-client/1.1" )
342351
352+ // ✅ 关键修复:使用更长的超时时间(10分钟)
353+ // 慢速网络下大文件分片上传可能需要很长时间
354+ client := & http.Client {
355+ Timeout : 10 * time .Minute ,
356+ Transport : base .HttpClient .Transport ,
357+ }
358+
343359 // 发送请求
344- resp , err := base . HttpClient .Do (req )
360+ resp , err := client .Do (req )
345361 if err != nil {
346362 return "" , err
347363 }
348364 defer resp .Body .Close ()
349365
366+ // 检查是否为URL过期错误(403, 410等状态码)
367+ if resp .StatusCode == 403 || resp .StatusCode == 410 {
368+ body , _ := io .ReadAll (resp .Body )
369+ return "" , fmt .Errorf ("upload url expired (status: %d): %s" , resp .StatusCode , string (body ))
370+ }
371+
372+ // ✅ 关键修复:409 PartAlreadyExist 不是错误!
373+ // 夸克使用Sequential模式,超时重试时如果分片已存在,说明第一次其实成功了
374+ if resp .StatusCode == 409 {
375+ body , _ := io .ReadAll (resp .Body )
376+ // 从响应体中提取已存在分片的ETag
377+ if strings .Contains (string (body ), "PartAlreadyExist" ) {
378+ // 尝试从XML响应中提取ETag
379+ if etag := extractEtagFromXML (string (body )); etag != "" {
380+ log .Infof ("[quark_open] Part %d already exists (409), using existing ETag: %s" , partNumber + 1 , etag )
381+ return etag , nil
382+ }
383+ // 如果无法提取ETag,返回错误
384+ log .Warnf ("[quark_open] Part %d already exists but cannot extract ETag from response: %s" , partNumber + 1 , string (body ))
385+ return "" , fmt .Errorf ("part already exists but ETag not found in response" )
386+ }
387+ }
388+
350389 if resp .StatusCode != 200 {
351390 body , _ := io .ReadAll (resp .Body )
352391 return "" , fmt .Errorf ("up status: %d, error: %s" , resp .StatusCode , string (body ))
@@ -355,6 +394,23 @@ func (d *QuarkOpen) upPart(ctx context.Context, upUrlInfo UpUrlInfo, partNumber
355394 return resp .Header .Get ("Etag" ), nil
356395}
357396
397+ // extractEtagFromXML 从OSS的XML错误响应中提取ETag
398+ // 示例: <PartEtag>"2F796AC486BB2891E3237D8BFDE020B5"</PartEtag>
399+ func extractEtagFromXML (xmlBody string ) string {
400+ start := strings .Index (xmlBody , "<PartEtag>" )
401+ if start == - 1 {
402+ return ""
403+ }
404+ start += len ("<PartEtag>" )
405+ end := strings .Index (xmlBody [start :], "</PartEtag>" )
406+ if end == - 1 {
407+ return ""
408+ }
409+ etag := xmlBody [start : start + end ]
410+ // 移除引号
411+ return strings .Trim (etag , "\" " )
412+ }
413+
358414func (d * QuarkOpen ) upFinish (ctx context.Context , pre UpPreResp , partInfo []base.Json , etags []string ) error {
359415 // 创建 part_info_list
360416 partInfoList := make ([]base.Json , len (partInfo ))
@@ -417,25 +473,36 @@ func (d *QuarkOpen) generateReqSign(method string, pathname string, signKey stri
417473}
418474
419475func (d * QuarkOpen ) refreshToken () error {
420- refresh , access , err := d ._refreshToken ()
476+ refresh , access , appID , signKey , err := d ._refreshToken ()
421477 for i := 0 ; i < 3 ; i ++ {
422478 if err == nil {
423479 break
424480 } else {
425481 log .Errorf ("[quark_open] failed to refresh token: %s" , err )
426482 }
427- refresh , access , err = d ._refreshToken ()
483+ refresh , access , appID , signKey , err = d ._refreshToken ()
428484 }
429485 if err != nil {
430486 return err
431487 }
432488 log .Infof ("[quark_open] token exchange: %s -> %s" , d .RefreshToken , refresh )
433489 d .RefreshToken , d .AccessToken = refresh , access
490+
491+ // 如果在线API返回了AppID和SignKey,保存它们(不为空时才更新)
492+ if appID != "" && appID != d .AppID {
493+ d .AppID = appID
494+ log .Infof ("[quark_open] AppID updated from online API: %s" , appID )
495+ }
496+ if signKey != "" && signKey != d .SignKey {
497+ d .SignKey = signKey
498+ log .Infof ("[quark_open] SignKey updated from online API" )
499+ }
500+
434501 op .MustSaveDriverStorage (d )
435502 return nil
436503}
437504
438- func (d * QuarkOpen ) _refreshToken () (string , string , error ) {
505+ func (d * QuarkOpen ) _refreshToken () (string , string , string , string , error ) {
439506 if d .UseOnlineAPI && d .APIAddress != "" {
440507 u := d .APIAddress
441508 var resp RefreshTokenOnlineAPIResp
@@ -448,19 +515,20 @@ func (d *QuarkOpen) _refreshToken() (string, string, error) {
448515 }).
449516 Get (u )
450517 if err != nil {
451- return "" , "" , err
518+ return "" , "" , "" , "" , err
452519 }
453520 if resp .RefreshToken == "" || resp .AccessToken == "" {
454521 if resp .ErrorMessage != "" {
455- return "" , "" , fmt .Errorf ("failed to refresh token: %s" , resp .ErrorMessage )
522+ return "" , "" , "" , "" , fmt .Errorf ("failed to refresh token: %s" , resp .ErrorMessage )
456523 }
457- return "" , "" , fmt .Errorf ("empty token returned from official API, a wrong refresh token may have been used" )
524+ return "" , "" , "" , "" , fmt .Errorf ("empty token returned from official API, a wrong refresh token may have been used" )
458525 }
459- return resp .RefreshToken , resp .AccessToken , nil
526+ // 返回所有字段,包括AppID和SignKey
527+ return resp .RefreshToken , resp .AccessToken , resp .AppID , resp .SignKey , nil
460528 }
461529
462530 // TODO 本地刷新逻辑
463- return "" , "" , fmt .Errorf ("local refresh token logic is not implemented yet, please use online API or contact the developer" )
531+ return "" , "" , "" , "" , fmt .Errorf ("local refresh token logic is not implemented yet, please use online API or contact the developer" )
464532}
465533
466534// 生成认证 Cookie
0 commit comments