diff --git a/vfs/s3fs.go b/vfs/s3fs.go index e5e0b35d..42bd126b 100644 --- a/vfs/s3fs.go +++ b/vfs/s3fs.go @@ -290,11 +290,19 @@ func (fs *S3Fs) Rename(source, target string) error { defer cancelFn() _, err = fs.svc.CopyObjectWithContext(ctx, &s3.CopyObjectInput{ Bucket: aws.String(fs.config.Bucket), - CopySource: aws.String(url.PathEscape(copySource)), + CopySource: aws.String(pathEscape(copySource)), Key: aws.String(target), StorageClass: util.NilIfEmpty(fs.config.StorageClass), ContentType: util.NilIfEmpty(contentType), }) + if err != nil { + metric.S3CopyObjectCompleted(err) + return err + } + err = fs.svc.WaitUntilObjectExistsWithContext(ctx, &s3.HeadObjectInput{ + Bucket: aws.String(fs.config.Bucket), + Key: aws.String(target), + }) metric.S3CopyObjectCompleted(err) if err != nil { return err @@ -698,3 +706,14 @@ func (*S3Fs) Close() error { func (*S3Fs) GetAvailableDiskSize(dirName string) (*sftp.StatVFS, error) { return nil, ErrStorageSizeUnavailable } + +// ideally we should simply use url.PathEscape: +// +// https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/go/example_code/s3/s3_copy_object.go#L65 +// +// but this cause issue with some vendors, see #483, the code below is copied from rclone +func pathEscape(in string) string { + var u url.URL + u.Path = in + return strings.ReplaceAll(u.String(), "+", "%2B") +}