seafile: fix corrupted uploads after a retried upload error
When an upload failed with a 500 error the upload was retried with a new upload link but the same input stream. The stream had already been consumed by the first attempt so the retry uploaded an empty file. This fixes it by returning a RetryError instead so the caller retries the upload with a fresh stream, which will fetch a new upload link.
This commit is contained in:
+15
-21
@@ -88,28 +88,22 @@ func (o *Object) Open(ctx context.Context, options ...fs.OpenOption) (io.ReadClo
|
||||
// But for unknown-sized objects (indicated by src.Size() == -1), Upload should either
|
||||
// return an error or update the object properly (rather than e.g. calling panic).
|
||||
func (o *Object) Update(ctx context.Context, in io.Reader, src fs.ObjectInfo, options ...fs.OpenOption) error {
|
||||
// The upload sometimes return a temporary 500 error
|
||||
// We cannot use the pacer to retry uploading the file as the upload link is single use only
|
||||
for retry := 0; retry <= 3; retry++ {
|
||||
uploadLink, err := o.fs.getUploadLink(ctx, o.libraryID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
uploaded, err := o.fs.upload(ctx, in, uploadLink, o.pathInLibrary)
|
||||
if err == ErrorInternalDuringUpload {
|
||||
// This is a temporary error, try again with a new upload link
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set the properties from the upload back to the object
|
||||
o.size = uploaded.Size
|
||||
|
||||
return nil
|
||||
uploadLink, err := o.fs.getUploadLink(ctx, o.libraryID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return ErrorInternalDuringUpload
|
||||
|
||||
// The upload can't be retried here as the input stream can't be re-read and
|
||||
// the upload link is single use, so upload returns a retry
|
||||
// error for the caller to retry with a fresh stream.
|
||||
uploaded, err := o.fs.upload(ctx, in, uploadLink, o.pathInLibrary)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set the properties from the upload back to the object
|
||||
o.size = uploaded.Size
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Remove this object
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/rclone/rclone/backend/seafile/api"
|
||||
"github.com/rclone/rclone/fs"
|
||||
"github.com/rclone/rclone/fs/fserrors"
|
||||
"github.com/rclone/rclone/lib/readers"
|
||||
"github.com/rclone/rclone/lib/rest"
|
||||
)
|
||||
@@ -721,8 +722,8 @@ func (f *Fs) upload(ctx context.Context, in io.Reader, uploadLink, filePath stri
|
||||
return nil, fs.ErrorPermissionDenied
|
||||
}
|
||||
if resp.StatusCode == 500 {
|
||||
// This is a temporary error - we will get a new upload link before retrying
|
||||
return nil, ErrorInternalDuringUpload
|
||||
// This is a temporary error - the caller will get a new upload link when it retries
|
||||
return nil, fserrors.RetryError(ErrorInternalDuringUpload)
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("failed to upload file: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user