8000 fix(139): handle upload file conflicts (#7692) · AlistGo/alist@bb2aec2 · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Commit bb2aec2

Browse files
authored
fix(139): handle upload file conflicts (#7692)
1 parent d7aa160 commit bb2aec2

File tree

2 files changed

+131
-61
lines changed

2 files changed

+131
-61
lines changed

drivers/139/driver.go

+130-61
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
552552
firstPartInfos = firstPartInfos[:100]
553553
}
554554

555-
// 获取上传信息和前100个分片的上传地址
555+
// 创建任务,获取上传信息和前100个分片的上传地址
556556
data := base.Json{
557557
"contentHash": fullHash,
558558
"contentHashAlgorithm": "SHA256",
@@ -572,87 +572,156 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
572572
return err
573573
}
574574

575-
if resp.Data.Exist || resp.Data.RapidUpload {
575+
// 判断文件是否已存在
576+
// resp.Data.Exist: true 已存在同名文件且校验相同,云端不会重复增加文件,无需手动处理冲突
577+
if resp.Data.Exist {
576578
return nil
577579
}
578580

579-
uploadPartInfos := resp.Data.PartInfos
581+
// 判断文件是否支持快传
582+
// resp.Data.RapidUpload: true 支持快传,但此处直接检测是否返回分片的上传地址
583+
// 快传的情况下同样需要手动处理冲突
584+
if resp.Data.PartInfos != nil {
585+
// 读取前100个分片的上传地址
586+
uploadPartInfos := resp.Data.PartInfos
587+
588+
// 获取后续分片的上传地址
589+
for i := 101; i < len(partInfos); i += 100 {
590 10000 +
end := i + 100
591+
if end > len(partInfos) {
592+
end = len(partInfos)
593+
}
594+
batchPartInfos := partInfos[i:end]
595+
596+
moredata := base.Json{
597+
"fileId": resp.Data.FileId,
598+
"uploadId": resp.Data.UploadId,
599+
"partInfos": batchPartInfos,
600+
"commonAccountInfo": base.Json{
601+
"account": d.Account,
602+
"accountType": 1,
603+
},
604+
}
605+
pathname := "/hcy/file/getUploadUrl"
606+
var moreresp PersonalUploadUrlResp
607+
_, err = d.personalPost(pathname, moredata, &moreresp)
608+
if err != nil {
609+
return err
610+
}
611+
uploadPartInfos = append(uploadPartInfos, moreresp.Data.PartInfos...)
612+
}
580613

581-
// 获取后续分片的上传地址
582-
for i := 101; i < len(partInfos); i += 100 {
583-
end := i + 100
584-
if end > len(partInfos) {
585-
end = len(partInfos)
614+
// Progress
615+
p := driver.NewProgress(stream.GetSize(), up)
616+
617+
// 上传所有分片
618+
for _, uploadPartInfo := range uploadPartInfos {
619+
index := uploadPartInfo.PartNumber - 1
620+
partSize := partInfos[index].PartSize
621+
log.Debugf("[139] uploading part %+v/%+v", index, len(uploadPartInfos))
622+
limitReader := io.LimitReader(stream, partSize)
623+
624+
// Update Progress
625+
r := io.TeeReader(limitReader, p)
626+
627+
req, err := http.NewRequest("PUT", uploadPartInfo.UploadUrl, r)
628+
if err != nil {
629+
return err
630+
}
631+
req = req.WithContext(ctx)
632+
req.Header.Set("Content-Type", "application/octet-stream")
633+
req.Header.Set("Content-Length", fmt.Sprint(partSize))
634+
req.Header.Set("Origin", "https://yun.139.com")
635+
req.Header.Set("Referer", "https://yun.139.com/")
636+
req.ContentLength = partSize
637+
638+
res, err := base.HttpClient.Do(req)
639+
if err != nil {
640+
return err
641+
}
642+
_ = res.Body.Close()
643+
log.Debugf("[139] uploaded: %+v", res)
644+
if res.StatusCode != http.StatusOK {
645+
return fmt.Errorf("unexpected status code: %d", res.StatusCode)
646+
}
586647
}
587-
batchPartInfos := partInfos[i:end]
588648

589-
moredata := base.Json{
590-
"fileId": resp.Data.FileId,
591-
"uploadId": resp.Data.UploadId,
592-
"partInfos": batchPartInfos,
593-
"commonAccountInfo": base.Json{
594-
"account": d.Account,
595-
"accountType": 1,
596-
},
649+
data = base.Json{
650+
"contentHash": fullHash,
651+
"contentHashAlgorithm": "SHA256",
652+
"fileId": resp.Data.FileId,
653+
"uploadId": resp.Data.UploadId,
597654
}
598-
pathname := "/hcy/file/getUploadUrl"
599-
var moreresp PersonalUploadUrlResp
600-
_, err = d.personalPost(pathname, moredata, &moreresp)
655+
_, err = d.personalPost("/hcy/file/complete", data, nil)
601656
if err != nil {
602657
return err
603658
}
604-
uploadPartInfos = append(uploadPartInfos, moreresp.Data.PartInfos...)
605659
}
606660

607-
// Progress
608-
p := driver.NewProgress(stream.GetSize(), up)
609-
610-
// 上传所有分片
611-
for _, uploadPartInfo := range uploadPartInfos {
612-
index := uploadPartInfo.PartNumber - 1
613-
partSize := partInfos[index].PartSize
614-
log.Debugf("[139] uploading part %+v/%+v", index, len(uploadPartInfos))
615-
limitReader := io.LimitReader(stream, partSize)
616-
617-
// Update Progress
618-
r := io.TeeReader(limitReader, p)
619-
620-
req, err := http.NewRequest("PUT", uploadPartInfo.UploadUrl, r)
661+
// 处理冲突
662+
if resp.Data.FileName != stream.GetName() {
663+
log.Debugf("[139] conflict detected: %s != %s", resp.Data.FileName, stream.GetName())
664+
// 给服务器一定时间处理数据,避免无法刷新文件列表
665+
time.Sleep(time.Millisecond * 500)
666+
// 刷新并获取文件列表
667+
files, err := d.List(ctx, dstDir, model.ListArgs{Refresh: true})
621668
if err != nil {
622669
return err
623670
}
624-
req = req.WithContext(ctx)
625-
req.Header.Set("Content-Type", "application/octet-stream")
626-
req.Header.Set("Content-Length", fmt.Sprint(partSize))
627-
req.Header.Set("Origin", "https://yun.139.com")
628-
req.Header.Set("Referer", "https://yun.139.com/")
629-
req.ContentLength = partSize
630-
631-
res, err := base.HttpClient.Do(req)
632-
if err != nil {
633-
return err
671+
// 删除旧文件
672+
for _, file := range files {
673+
if file.GetName() == stream.GetName() {
674+
log.Debugf("[139] conflict: removing old: %s", file.GetName())
675+
// 删除前重命名旧文件,避免仍旧冲突
676+
err = d.Rename(ctx, file, stream.GetName()+random.String(4))
677+
if err != nil {
678+
return err
679+
}
680+
err = d.Remove(ctx, file)
681+
if err != nil {
682+
return err
683+
}
684+
break
685+
}
634686
}
635-
_ = res.Body.Close()
636-
log.Debugf("[139] uploaded: %+v", res)
637-
if res.StatusCode != http.StatusOK {
638-
return fmt.Errorf("unexpected status code: %d", res.StatusCode)
687+
// 重命名新文件
688+
for _, file := range files {
689+
if file.GetName() == resp.Data.FileName {
690+
log.Debugf("[139] conflict: renaming new: %s => %s", file.GetName(), stream.GetName())
691+
err = d.Rename(ctx, file, stream.GetName())
692+
if err != nil {
693+
return err
694+
}
695+
break
696+
}
639697
}
640698
}
641-
642-
data = base.Json{
643-
"contentHash": fullHash,
644-
"contentHashAlgorithm": "SHA256",
645-
"fileId": resp.Data.FileId,
646-
"uploadId": resp.Data.UploadId,
647-
}
648-
_, err = d.personalPost("/hcy/file/complete", data, nil)
649-
if err != nil {
650-
return err
651-
}
652699
return nil
653700
case MetaPersonal:
654701
fallthrough
655702
case MetaFamily:
703+
// 处理冲突
704+
// 获取文件列表
705+
files, err := d.List(ctx, dstDir, model.ListArgs{})
706+
if err != nil {
707+
return err
708+
}
709+
// 删除旧文件
710+
for _, file := range files {
711+
if file.GetName() == stream.GetName() {
712+
log.Debugf("[139] conflict: removing old: %s", file.GetName())
713+
// 删除前重命名旧文件,避免仍旧冲突
714+
err = d.Rename(ctx, file, stream.GetName()+random.String(4))
715+
if err != nil {
716+
return err
717+
}
718+
err = d.Remove(ctx, file)
719+
if err != nil {
720+
return err
721+
}
722+
break
723+
}
724+
}
656725
data := base.Json{
657726
"manualRename": 2,
658727
"operation": 0,
@@ -688,7 +757,7 @@ func (d *Yun139) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
688757
pathname = "/orchestration/familyCloud-rebuild/content/v1.0/getFileUploadURL"
689758
}
690759
var resp UploadResp
691-
_, err := d.post(pathname, data, &resp)
760+
_, err = d.post(pathname, data, &resp)
692761
if err != nil {
693762
return err
694763
}

drivers/139/types.go

+1
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ type PersonalUploadResp struct {
261261
BaseResp
262262
Data struct {
263263
FileId string `json:"fileId"`
264+
FileName string `json:"fileName"`
264265
PartInfos []PersonalPartInfo `json:"partInfos"`
265266
Exist bool `json:"exist"`
266267
RapidUpload bool `json:"rapidUpload"`

0 commit comments

Comments
 (0)
0