diff --git a/src/Renci.SshNet/ISftpClient.cs b/src/Renci.SshNet/ISftpClient.cs
index af6d31b94..759d4ecbe 100644
--- a/src/Renci.SshNet/ISftpClient.cs
+++ b/src/Renci.SshNet/ISftpClient.cs
@@ -1133,6 +1133,22 @@ public interface ISftpClient : IBaseClient
/// The method was called after the client was disposed.
Task UploadFileAsync(Stream input, string path, CancellationToken cancellationToken = default);
+ ///
+ /// Asynchronously uploads a to a remote file path.
+ ///
+ /// The to write to the remote path.
+ /// The remote file path to write to.
+ /// Whether the remote file can be overwritten if it already exists.
+ /// The to observe.
+ /// A that represents the asynchronous upload operation.
+ /// or is .
+ /// is empty or contains only whitespace characters.
+ /// Client is not connected.
+ /// Permission to upload the file was denied by the remote host. -or- An SSH command was denied by the server.
+ /// An SSH error where is the message from the remote host.
+ /// The method was called after the client was disposed.
+ Task UploadFileAsync(Stream input, string path, bool canOverride, CancellationToken cancellationToken = default);
+
///
/// Writes the specified byte array to the specified file, and closes the file.
///
diff --git a/src/Renci.SshNet/SftpClient.cs b/src/Renci.SshNet/SftpClient.cs
index 69e739b29..afd047108 100644
--- a/src/Renci.SshNet/SftpClient.cs
+++ b/src/Renci.SshNet/SftpClient.cs
@@ -1077,15 +1077,32 @@ public void UploadFile(Stream input, string path, bool canOverride, Action
public Task UploadFileAsync(Stream input, string path, CancellationToken cancellationToken = default)
+ {
+ return UploadFileAsync(input, path, canOverride: true, cancellationToken);
+ }
+
+ ///
+ public Task UploadFileAsync(Stream input, string path, bool canOverride, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(input);
ArgumentException.ThrowIfNullOrWhiteSpace(path);
CheckDisposed();
+ var flags = Flags.Write | Flags.Truncate;
+
+ if (canOverride)
+ {
+ flags |= Flags.CreateNewOrOpen;
+ }
+ else
+ {
+ flags |= Flags.CreateNew;
+ }
+
return InternalUploadFile(
input,
path,
- Flags.Write | Flags.Truncate | Flags.CreateNewOrOpen,
+ flags,
asyncResult: null,
uploadCallback: null,
isAsync: true,
diff --git a/test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs b/test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs
index e141f19d1..99af3a370 100644
--- a/test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs
+++ b/test/Renci.SshNet.IntegrationTests/OldIntegrationTests/SftpClientTest.Upload.cs
@@ -74,6 +74,18 @@ public async Task Test_Sftp_Upload_And_Download_Async_1MB_File()
await sftp.UploadFileAsync(file, remoteFileName).ConfigureAwait(false);
}
+ // uploading again should not throw because of the default canOverride = true
+ using (var file = File.OpenRead(uploadedFileName))
+ {
+ await sftp.UploadFileAsync(file, remoteFileName).ConfigureAwait(false);
+ }
+
+ // uploading with canOverride = false should throw because the file already exists
+ using (var file = File.OpenRead(uploadedFileName))
+ {
+ await Assert.ThrowsAsync(async () => await sftp.UploadFileAsync(file, remoteFileName, canOverride: false).ConfigureAwait(false));
+ }
+
var downloadedFileName = Path.GetTempFileName();
using (var file = File.OpenWrite(downloadedFileName))