mirror of
https://github.com/sshnet/SSH.NET.git
synced 2026-09-10 09:15:47 +00:00
ListDirectoryAsync return IAsyncEnumerable (#1126)
* ListDirectoryAsync return IAsyncEnumerable * Fix documentation * Update README.md * Fix * Add Sftp ListDirectoryAsync test * Revert * Integration tests for ListDirectoryAsync with IAsyncEnumerable
This commit is contained in:
committed by
GitHub
parent
1b46264bd3
commit
7cd0487785
@@ -117,7 +117,7 @@ Private keys can be encrypted using one of the following cipher methods:
|
||||
## Framework Support
|
||||
**SSH.NET** supports the following target frameworks:
|
||||
* .NETFramework 4.6.2 (and higher)
|
||||
* .NET Standard 2.0
|
||||
* .NET Standard 2.0 and 2.1
|
||||
* .NET 6 (and higher)
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
<OutputDirectory>Renci.SshNet\bin\$(Configuration)\netstandard2.0</OutputDirectory>
|
||||
<Moniker>netstandard2.0</Moniker>
|
||||
</TargetFrameworkModern>
|
||||
<TargetFrameworkModern Include=".NETStandard 2.1">
|
||||
<OutputDirectory>Renci.SshNet\bin\$(Configuration)\netstandard2.1</OutputDirectory>
|
||||
<Moniker>netstandard2.1</Moniker>
|
||||
</TargetFrameworkModern>
|
||||
<TargetFrameworkModern Include=".NET 6.0">
|
||||
<OutputDirectory>Renci.SshNet\bin\$(Configuration)\net6.0</OutputDirectory>
|
||||
<Moniker>net6.0</Moniker>
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
<group targetFramework="net462" />
|
||||
<group targetFramework="netstandard2.0">
|
||||
<dependency id="SshNet.Security.Cryptography" version="[1.3.0]" />
|
||||
</group>
|
||||
<group targetFramework="netstandard2.1">
|
||||
<dependency id="SshNet.Security.Cryptography" version="[1.3.0]" />
|
||||
</group>
|
||||
<group targetFramework="net6.0">
|
||||
<dependency id="SshNet.Security.Cryptography" version="[1.3.0]" />
|
||||
|
||||
@@ -67,17 +67,17 @@ namespace IntegrationTests
|
||||
Assert.IsTrue(_sftpClient.Exists(testFilePath));
|
||||
|
||||
// Check if ListDirectory works
|
||||
var files = await _sftpClient.ListDirectoryAsync(testDirectory, CancellationToken.None);
|
||||
|
||||
_sftpClient.DeleteFile(testFilePath);
|
||||
_sftpClient.DeleteDirectory(testDirectory);
|
||||
var files = _sftpClient.ListDirectoryAsync(testDirectory, CancellationToken.None);
|
||||
|
||||
var builder = new StringBuilder();
|
||||
foreach (var file in files)
|
||||
await foreach (var file in files)
|
||||
{
|
||||
builder.AppendLine($"{file.FullName} {file.IsRegularFile} {file.IsDirectory}");
|
||||
}
|
||||
|
||||
_sftpClient.DeleteFile(testFilePath);
|
||||
_sftpClient.DeleteDirectory(testDirectory);
|
||||
|
||||
Assert.AreEqual(@"/home/sshnet/sshnet-test/. False True
|
||||
/home/sshnet/sshnet-test/.. False True
|
||||
/home/sshnet/sshnet-test/test-file.txt True False
|
||||
|
||||
@@ -4,6 +4,10 @@ using Renci.SshNet.Tests.Properties;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
#if NET6_0_OR_GREATER
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
|
||||
namespace Renci.SshNet.Tests.Classes
|
||||
{
|
||||
@@ -89,6 +93,30 @@ namespace Renci.SshNet.Tests.Classes
|
||||
}
|
||||
}
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
[TestMethod]
|
||||
[TestCategory("Sftp")]
|
||||
[TestCategory("integration")]
|
||||
public async Task Test_Sftp_ListDirectoryAsync_Current()
|
||||
{
|
||||
using (var sftp = new SftpClient(Resources.HOST, Resources.USERNAME, Resources.PASSWORD))
|
||||
{
|
||||
sftp.Connect();
|
||||
var cts = new CancellationTokenSource();
|
||||
cts.CancelAfter(TimeSpan.FromMinutes(1));
|
||||
var count = 0;
|
||||
await foreach(var file in sftp.ListDirectoryAsync(".", cts.Token))
|
||||
{
|
||||
count++;
|
||||
Debug.WriteLine(file.FullName);
|
||||
}
|
||||
|
||||
Assert.IsTrue(count > 0);
|
||||
|
||||
sftp.Disconnect();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
[TestMethod]
|
||||
[TestCategory("Sftp")]
|
||||
[TestCategory("integration")]
|
||||
@@ -265,4 +293,4 @@ namespace Renci.SshNet.Tests.Classes
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Renci.SshNet
|
||||
/// SSH_FXP_DATA protocol fields.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The size of the each indivual SSH_FXP_DATA message is limited to the
|
||||
/// The size of the each individual SSH_FXP_DATA message is limited to the
|
||||
/// local maximum packet size of the channel, which is set to <c>64 KB</c>
|
||||
/// for SSH.NET. However, the peer can limit this even further.
|
||||
/// </para>
|
||||
@@ -699,21 +699,23 @@ namespace Renci.SshNet
|
||||
/// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>
|
||||
IEnumerable<ISftpFile> ListDirectory(string path, Action<int> listCallback = null);
|
||||
|
||||
#if FEATURE_ASYNC_ENUMERABLE
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves list of files in remote directory.
|
||||
/// Asynchronously enumerates the files in remote directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Task{IEnumerable}"/> that represents the asynchronous list operation.
|
||||
/// The task result contains an enumerable collection of <see cref="SftpFile"/> for the files in the directory specified by <paramref name="path" />.
|
||||
/// An <see cref="IAsyncEnumerable{T}"/> of <see cref="ISftpFile"/> that represents the asynchronous enumeration operation.
|
||||
/// The enumeration contains an async stream of <see cref="ISftpFile"/> for the files in the directory specified by <paramref name="path" />.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path" /> is <b>null</b>.</exception>
|
||||
/// <exception cref="SshConnectionException">Client is not connected.</exception>
|
||||
/// <exception cref="SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</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>
|
||||
Task<IEnumerable<ISftpFile>> ListDirectoryAsync(string path, CancellationToken cancellationToken);
|
||||
IAsyncEnumerable<ISftpFile> ListDirectoryAsync(string path, CancellationToken cancellationToken);
|
||||
#endif //FEATURE_ASYNC_ENUMERABLE
|
||||
|
||||
/// <summary>
|
||||
/// Opens a <see cref="SftpFileStream"/> on the specified path with read/write access.
|
||||
|
||||
@@ -2,18 +2,22 @@
|
||||
<PropertyGroup>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<AssemblyName>Renci.SshNet</AssemblyName>
|
||||
<TargetFrameworks>net462;netstandard2.0;net6.0;net7.0</TargetFrameworks>
|
||||
<TargetFrameworks>net462;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'net462' ">
|
||||
<DefineConstants>FEATURE_BINARY_SERIALIZATION;FEATURE_SOCKET_EAP;FEATURE_SOCKET_APM;FEATURE_DNS_SYNC;FEATURE_HASH_RIPEMD160_CREATE;FEATURE_HMAC_RIPEMD160</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
|
||||
<PackageReference Include="SshNet.Security.Cryptography" Version="[1.3.0]" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
|
||||
<DefineConstants>FEATURE_SOCKET_TAP;FEATURE_SOCKET_APM;FEATURE_SOCKET_EAP;FEATURE_DNS_SYNC;FEATURE_DNS_APM;FEATURE_DNS_TAP</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' ">
|
||||
<DefineConstants>$(DefineConstants);FEATURE_ASYNC_ENUMERABLE</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -10,6 +10,9 @@ using Renci.SshNet.Abstractions;
|
||||
using Renci.SshNet.Common;
|
||||
using Renci.SshNet.Sftp;
|
||||
using System.Threading.Tasks;
|
||||
#if FEATURE_ASYNC_ENUMERABLE
|
||||
using System.Runtime.CompilerServices;
|
||||
#endif
|
||||
|
||||
namespace Renci.SshNet
|
||||
{
|
||||
@@ -92,7 +95,7 @@ namespace Renci.SshNet
|
||||
/// SSH_FXP_DATA protocol fields.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The size of the each indivual SSH_FXP_DATA message is limited to the
|
||||
/// The size of the each individual SSH_FXP_DATA message is limited to the
|
||||
/// local maximum packet size of the channel, which is set to <c>64 KB</c>
|
||||
/// for SSH.NET. However, the peer can limit this even further.
|
||||
/// </para>
|
||||
@@ -584,21 +587,22 @@ namespace Renci.SshNet
|
||||
return InternalListDirectory(path, listCallback);
|
||||
}
|
||||
|
||||
#if FEATURE_ASYNC_ENUMERABLE
|
||||
/// <summary>
|
||||
/// Asynchronously retrieves list of files in remote directory.
|
||||
/// Asynchronously enumerates the files in remote directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
|
||||
/// <returns>
|
||||
/// A <see cref="Task{IEnumerable}"/> that represents the asynchronous list operation.
|
||||
/// The task result contains an enumerable collection of <see cref="SftpFile"/> for the files in the directory specified by <paramref name="path" />.
|
||||
/// An <see cref="IAsyncEnumerable{T}"/> of <see cref="ISftpFile"/> that represents the asynchronous enumeration operation.
|
||||
/// The enumeration contains an async stream of <see cref="ISftpFile"/> for the files in the directory specified by <paramref name="path" />.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="path" /> is <b>null</b>.</exception>
|
||||
/// <exception cref="SshConnectionException">Client is not connected.</exception>
|
||||
/// <exception cref="SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</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>
|
||||
public async Task<IEnumerable<ISftpFile>> ListDirectoryAsync(string path, CancellationToken cancellationToken)
|
||||
public async IAsyncEnumerable<ISftpFile> ListDirectoryAsync(string path, [EnumeratorCancellation] CancellationToken cancellationToken)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
@@ -616,7 +620,6 @@ namespace Renci.SshNet
|
||||
|
||||
var fullPath = await _sftpSession.GetCanonicalPathAsync(path, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var result = new List<SftpFile>();
|
||||
var handle = await _sftpSession.RequestOpenDirAsync(fullPath, cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
@@ -634,18 +637,16 @@ namespace Renci.SshNet
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
result.Add(new SftpFile(_sftpSession, basePath + file.Key, file.Value));
|
||||
yield return new SftpFile(_sftpSession, basePath + file.Key, file.Value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
await _sftpSession.RequestCloseAsync(handle, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif //FEATURE_ASYNC_ENUMERABLE
|
||||
|
||||
/// <summary>
|
||||
/// Begins an asynchronous operation of retrieving list of files in remote directory.
|
||||
|
||||
Reference in New Issue
Block a user