Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds DownloadFileAsync, UploadFileAsync and SynchronizeDirectoriesAsync overloads #1515

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
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
55 changes: 55 additions & 0 deletions src/Renci.SshNet/ISftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,30 @@ public interface ISftpClient : IBaseClient
/// </remarks>
void DownloadFile(string path, Stream output, Action<ulong>? downloadCallback = null);

/// <summary>
/// Asynchronously downloads remote file specified by the path into the stream.
/// </summary>
/// <param name="path">File to download.</param>
/// <param name="output">Stream to write the file into.</param>
/// <param name="downloadCallback">The download callback.</param>
/// <param name="factory">The <see cref="TaskFactory">TaskFactory</see> used to create the Task.</param>
/// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
/// created <see cref="Task">Task</see>.</param>
/// <param name="scheduler">The <see cref="TaskScheduler">TaskScheduler</see>
/// that is used to schedule the task that executes the end method.</param>
/// <exception cref="ArgumentNullException"><paramref name="output" /> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="path" /> is <see langword="null"/> or contains only whitespace characters.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to perform the operation was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>///
/// <exception cref="SshException">A 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>
/// <remarks>
/// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
/// </remarks>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task DownloadFileAsync(string path, Stream output, Action<ulong>? downloadCallback = null, TaskFactory? factory = null, TaskCreationOptions creationOptions = default, TaskScheduler? scheduler = null);

/// <summary>
/// Ends an asynchronous file downloading into the stream.
/// </summary>
Expand Down Expand Up @@ -1038,6 +1062,22 @@ public interface ISftpClient : IBaseClient
/// <exception cref="SshException">If a problem occurs while copying the file.</exception>
IEnumerable<FileInfo> SynchronizeDirectories(string sourcePath, string destinationPath, string searchPattern);

/// <summary>
/// Asynchronously synchronizes the directories.
/// </summary>
/// <param name="sourcePath">The source path.</param>
/// <param name="destinationPath">The destination path.</param>
/// <param name="searchPattern">The search pattern.</param>
/// <param name="factory">The <see cref="TaskFactory">TaskFactory</see> used to create the Task.</param>
/// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
/// created <see cref="Task">Task</see>.</param>
/// <param name="scheduler">The <see cref="TaskScheduler">TaskScheduler</see>
/// that is used to schedule the task that executes the end method.</param>
/// <returns>
/// A list of uploaded files.
/// </returns>
Task<IEnumerable<FileInfo>> SynchronizeDirectoriesAsync(string sourcePath, string destinationPath, string searchPattern, TaskFactory<IEnumerable<FileInfo>>? factory = null, TaskCreationOptions creationOptions = default, TaskScheduler? scheduler = null);

/// <summary>
/// Uploads stream into remote file.
/// </summary>
Expand Down Expand Up @@ -1073,6 +1113,21 @@ public interface ISftpClient : IBaseClient
/// </remarks>
void UploadFile(Stream input, string path, bool canOverride, Action<ulong>? uploadCallback = null);

/// <summary>
/// Asynchronously upload the stream into the remote file.
/// </summary>
/// <param name="input">Data input stream.</param>
/// <param name="path">Remote file path.</param>
/// <param name="canOverride">if set to <see langword="true"/> then existing file will be overwritten.</param>
/// <param name="uploadCallback">The upload callback.</param>
/// <param name="factory">The <see cref="TaskFactory">TaskFactory</see> used to create the Task.</param>
/// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
/// created <see cref="Task">Task</see>.</param>
/// <param name="scheduler">The <see cref="TaskScheduler">TaskScheduler</see>
/// that is used to schedule the task that executes the end method.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
Task UploadFileAsync(Stream input, string path, bool canOverride = false, Action<ulong>? uploadCallback = null, TaskFactory? factory = null, TaskCreationOptions creationOptions = default, TaskScheduler? scheduler = null);

/// <summary>
/// Writes the specified byte array to the specified file, and closes the file.
/// </summary>
Expand Down
82 changes: 82 additions & 0 deletions src/Renci.SshNet/SftpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,39 @@ public void DownloadFile(string path, Stream output, Action<ulong>? downloadCall
InternalDownloadFile(path, output, asyncResult: null, downloadCallback);
}

/// <summary>
/// Asynchronously downloads remote file specified by the path into the stream.
/// </summary>
/// <param name="path">File to download.</param>
/// <param name="output">Stream to write the file into.</param>
/// <param name="downloadCallback">The download callback.</param>
/// <param name="factory">The <see cref="TaskFactory">TaskFactory</see> used to create the Task.</param>
/// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
/// created <see cref="Task">Task</see>.</param>
/// <param name="scheduler">The <see cref="TaskScheduler">TaskScheduler</see>
/// that is used to schedule the task that executes the end method.</param>
/// <exception cref="ArgumentNullException"><paramref name="output" /> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentException"><paramref name="path" /> is <see langword="null"/> or contains only whitespace characters.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
/// <exception cref="SftpPermissionDeniedException">Permission to perform the operation was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
/// <exception cref="SftpPathNotFoundException"><paramref name="path"/> was not found on the remote host.</exception>///
/// <exception cref="SshException">A 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>
/// <remarks>
/// Method calls made by this method to <paramref name="output" />, may under certain conditions result in exceptions thrown by the stream.
/// </remarks>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public Task DownloadFileAsync(string path, Stream output, Action<ulong>? downloadCallback = null, TaskFactory? factory = null, TaskCreationOptions creationOptions = default, TaskScheduler? scheduler = null)
{
var taskFactory = factory ?? Task.Factory;

return taskFactory.FromAsync(
BeginDownloadFile(path, output, asyncCallback: null, state: null, downloadCallback),
EndDownloadFile,
creationOptions,
scheduler ?? taskFactory.Scheduler ?? TaskScheduler.Current);
}

/// <summary>
/// Begins an asynchronous file downloading into the stream.
/// </summary>
Expand Down Expand Up @@ -1077,6 +1110,30 @@ public void UploadFile(Stream input, string path, bool canOverride, Action<ulong
InternalUploadFile(input, path, flags, asyncResult: null, uploadCallback);
}

/// <summary>
/// Asynchronously upload the stream into the remote file.
/// </summary>
/// <param name="input">Data input stream.</param>
/// <param name="path">Remote file path.</param>
/// <param name="canOverride">if set to <see langword="true"/> then existing file will be overwritten.</param>
/// <param name="uploadCallback">The upload callback.</param>
/// <param name="factory">The <see cref="TaskFactory">TaskFactory</see> used to create the Task.</param>
/// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
/// created <see cref="Task">Task</see>.</param>
/// <param name="scheduler">The <see cref="TaskScheduler">TaskScheduler</see>
/// that is used to schedule the task that executes the end method.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public Task UploadFileAsync(Stream input, string path, bool canOverride = false, Action<ulong>? uploadCallback = null, TaskFactory? factory = null, TaskCreationOptions creationOptions = default, TaskScheduler? scheduler = null)
{
var taskFactory = factory ?? Task.Factory;

return taskFactory.FromAsync(
BeginUploadFile(input, path, canOverride, asyncCallback: null, state: null, uploadCallback),
EndUploadFile,
creationOptions,
scheduler ?? taskFactory.Scheduler ?? TaskScheduler.Current);
}

/// <summary>
/// Begins an asynchronous uploading the stream into remote file.
/// </summary>
Expand Down Expand Up @@ -2167,6 +2224,31 @@ public IEnumerable<FileInfo> SynchronizeDirectories(string sourcePath, string de
return InternalSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asynchResult: null);
}

/// <summary>
/// Asynchronously synchronizes the directories.
/// </summary>
/// <param name="sourcePath">The source path.</param>
/// <param name="destinationPath">The destination path.</param>
/// <param name="searchPattern">The search pattern.</param>
/// <param name="factory">The <see cref="TaskFactory">TaskFactory</see> used to create the Task.</param>
/// <param name="creationOptions">The TaskCreationOptions value that controls the behavior of the
/// created <see cref="Task">Task</see>.</param>
/// <param name="scheduler">The <see cref="TaskScheduler">TaskScheduler</see>
/// that is used to schedule the task that executes the end method.</param>
/// <returns>
/// A list of uploaded files.
/// </returns>
public Task<IEnumerable<FileInfo>> SynchronizeDirectoriesAsync(string sourcePath, string destinationPath, string searchPattern, TaskFactory<IEnumerable<FileInfo>>? factory = null, TaskCreationOptions creationOptions = default, TaskScheduler? scheduler = null)
{
var taskFactory = factory ?? Task<IEnumerable<FileInfo>>.Factory;

return taskFactory.FromAsync(
BeginSynchronizeDirectories(sourcePath, destinationPath, searchPattern, asyncCallback: null, state: null),
EndSynchronizeDirectories,
creationOptions,
scheduler ?? taskFactory?.Scheduler ?? TaskScheduler.Current);
}

/// <summary>
/// Begins the synchronize directories.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,9 @@ public async Task Test_Get_FileAsync()
{
sftp.Connect();

#pragma warning disable S6966
sftp.UploadFile(new MemoryStream(), "abc.txt");
#pragma warning restore S6966

var file = await sftp.GetAsync("abc.txt", default).ConfigureAwait(false);

Expand Down Expand Up @@ -157,7 +159,9 @@ public async Task Test_Get_International_FileAsync()
{
sftp.Connect();

#pragma warning disable S6966
sftp.UploadFile(new MemoryStream(), "test-üöä-");
#pragma warning restore S6966

var file = await sftp.GetAsync("test-üöä-", default).ConfigureAwait(false);

Expand Down
6 changes: 3 additions & 3 deletions test/Renci.SshNet.IntegrationTests/SftpClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public async Task Create_directory_with_contents_and_list_it_async()

// Upload file and check if it exists
using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
_sftpClient.UploadFile(fileStream, testFilePath);
await _sftpClient.UploadFileAsync(fileStream, testFilePath);
Assert.IsTrue(await _sftpClient.ExistsAsync(testFilePath));

// Check if ListDirectory works
Expand Down Expand Up @@ -124,7 +124,7 @@ public async Task Create_directory_with_contents_and_delete_contents_then_direct

// Upload file and check if it exists
using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
_sftpClient.UploadFile(fileStream, testFilePath);
await _sftpClient.UploadFileAsync(fileStream, testFilePath);
Assert.IsTrue(await _sftpClient.ExistsAsync(testFilePath).ConfigureAwait(false));

await _sftpClient.DeleteFileAsync(testFilePath, CancellationToken.None).ConfigureAwait(false);
Expand Down Expand Up @@ -159,7 +159,7 @@ public async Task Create_file_and_delete_using_DeleteAsync()

// Upload file and check if it exists
using var fileStream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
_sftpClient.UploadFile(fileStream, testFileName);
await _sftpClient.UploadFileAsync(fileStream, testFileName);
Assert.IsTrue(await _sftpClient.ExistsAsync(testFileName).ConfigureAwait(false));

await _sftpClient.DeleteAsync(testFileName, CancellationToken.None).ConfigureAwait(false);
Expand Down
4 changes: 2 additions & 2 deletions test/Renci.SshNet.IntegrationTests/SftpTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4114,9 +4114,9 @@ public async Task Sftp_ChangeDirectory_DirectoryExistsAsync()
using (var uploadStream = CreateMemoryStream(100))
{
uploadStream.Position = 0;

#pragma warning disable S6966
client.UploadFile(uploadStream, "gert.txt");

#pragma warning restore S6966
uploadStream.Position = 0;

using (var downloadStream = client.OpenRead(remoteDirectory + "/gert.txt"))
Expand Down