Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Renci.SshNet/ISftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,22 @@ public interface ISftpClient : IBaseClient
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
Task UploadFileAsync(Stream input, string path, CancellationToken cancellationToken = default);

/// <summary>
/// Asynchronously uploads a <see cref="Stream"/> to a remote file path.
/// </summary>
/// <param name="input">The <see cref="Stream"/> to write to the remote path.</param>
/// <param name="path">The remote file path to write to.</param>
/// <param name="canOverride">Whether the remote file can be overwritten if it already exists.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
/// <returns>A <see cref="Task"/> that represents the asynchronous upload operation.</returns>
/// <exception cref="ArgumentNullException"><paramref name="input"/> or <paramref name="path"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="path" /> is empty or contains only whitespace characters.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to upload the file was denied by the remote host. <para>-or-</para> An SSH command was denied by the server.</exception>
/// <exception cref="SshException">An SSH error where <see cref="Exception.Message" /> is the message from the remote host.</exception>
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
Task UploadFileAsync(Stream input, string path, bool canOverride, CancellationToken cancellationToken = default);

/// <summary>
/// Writes the specified byte array to the specified file, and closes the file.
/// </summary>
Expand Down
19 changes: 18 additions & 1 deletion src/Renci.SshNet/SftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1077,15 +1077,32 @@ public void UploadFile(Stream input, string path, bool canOverride, Action<ulong

/// <inheritdoc />
public Task UploadFileAsync(Stream input, string path, CancellationToken cancellationToken = default)
{
return UploadFileAsync(input, path, canOverride: true, cancellationToken);
}

/// <inheritdoc />
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<SftpException>(async () => await sftp.UploadFileAsync(file, remoteFileName, canOverride: false).ConfigureAwait(false));
}

var downloadedFileName = Path.GetTempFileName();

using (var file = File.OpenWrite(downloadedFileName))
Expand Down
Loading