C# mReader(远程文件流)) { 使用(var csv=新的CsvReader(读卡器)) { /* //CSV解析示例: var记录=新列表(); csv.Read(); csv.ReadHeader(); 而(csv.Read()) { var记录=新的Foo { Id=csv.GetField(“Id”), Name=csv.GetField(“名称”) }; 记录。添加(记录); } */ } } } } 最后{ sftp.Disconnect(); } } }
使用SftpClient池的修改版本 看 池的实施借用自:C# mReader(远程文件流)) { 使用(var csv=新的CsvReader(读卡器)) { /* //CSV解析示例: var记录=新列表(); csv.Read(); csv.ReadHeader(); 而(csv.Read()) { var记录=新的Foo { Id=csv.GetField(“Id”), Name=csv.GetField(“名称”) }; 记录。添加(记录); } */ } } } } 最后{ sftp.Disconnect(); } } },c#,C#,使用SftpClient池的修改版本 看 池的实施借用自: // ///从[How to:通过使用 ///[购物袋](https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-create-an-object-pool). /// /// 公共类对象池:IDisposable 其中T:IDisposable { 私有只读函数对象生成器; 私有只读ConcurrentBag_对象; 公共对象池
//
///从[How to:通过使用
///[购物袋](https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-create-an-object-pool).
///
///
公共类对象池:IDisposable
其中T:IDisposable
{
私有只读函数对象生成器;
私有只读ConcurrentBag_对象;
公共对象池(Func对象生成器)
{
_objectGenerator=objectGenerator??抛出新ArgumentNullException(nameof(objectGenerator));
_对象=新的ConcurrentBag();
}
公共空间处置()
{
while(_objects.TryTake(out var项))
{
item.Dispose();
}
}
公共T GetObject()
{
返回_objects.TryTake(out var item)?item:_objectGenerator();
}
公共对象(T项)
{
_对象。添加(项);
}
}
最简单的基于池的实现(它不关心异常处理、重试策略):
内部类SFTPClient
{
私有只读对象池\u对象池;
公共SFTPClient(凭据)
{
_objectPool=新的objectPool(()=>
{
var client=新的SftpClient(credentials.host、credentials.username、credentials.password);
client.Connect();
返回客户;
});
}
public void GetDirectoryList()
{
var client=_objectPool.GetObject();
尝试
{
//client.ListDirectory()。。
}
最后
{
如果(客户端已断开连接)
{
_objectPool.PutObject(客户端);
}
}
}
公共异步任务ProcessRemoteFileAsync()
{
var filepath=新列表();
//正在初始化文件路径。。
var tasks=文件路径
.Select(f=>ParseRemoteFileAsync(f))
.ToArray();
var results=await Task.WhenAll(tasks).ConfigureAwait(false);
//遍历结果。。
}
公共任务ParseRemoteFileAsync(字符串文件路径)
{
var client=_objectPool.GetObject();
尝试
{
使用(var remoteFileStream=client.OpenRead(filePath))
{
使用(var reader=newstreamreader(remoteFileStream))
{
使用(var csv=新的CsvReader(读卡器))
{
// ..
}
}
返回Task.FromResult(新文件内容());
}
}
最后
{
如果(客户端已断开连接)
{
_objectPool.PutObject(客户端);
}
}
}
}
您需要使用内存流。然后从SFTP填充内存流。我会编写自己的代码来读取csv而不是使用CSVHelper。使用内存流而不是将文件保存到本地然后打开它有什么缺点吗?:)不会。内存流将更有效,因为您只需执行一个步骤而不是两个步骤(写入文件,然后读取文件)。内存流相当于流编写器或流读取器。@jdweng我想异步执行此方法。可能吗?因为许多国家有很多目录,我希望每个国家都能下载文件并并行解析CSV。一些服务器限制来自同一IP的连接数,因此可能无法实现。您需要使用内存流。然后从SFTP填充内存流。我会编写自己的代码来读取csv而不是使用CSVHelper。使用内存流而不是将文件保存到本地然后打开它有什么缺点吗?:)不会。内存流将更有效,因为您只需执行一个步骤而不是两个步骤(写入文件,然后读取文件)。内存流相当于流编写器或流读取器。@jdweng我想异步执行此方法。可能吗?因为有很多国家的目录,我希望每个国家下载文件并并行解析CSV。一些服务器限制来自同一IP的连接数,因此可能无法实现。我是否可以将ReadAndParseCSV逻辑放在自己的函数中,并在using csvreader中调用它?是的,您可以将其移动到单独的方法。我希望异步执行此方法。可能吗?因为有许多国家的目录,我希望每个国家下载文件并解析CSV并行。这可能吗?我添加了多文件处理的示例,它应该可以帮助您自己开始;)谢谢,有件事我很好奇。我得到了一个名为GetDirectoryListFromSFTP()的函数,它创建了到与ReadRemoteDirectory相同的SFTP的连接,是否可以在另一个文件夹中创建此连接->获取目录列表->关闭连接->打开连接
public async Task ProcessRemoteFilesAsync()
{
var credentials = new Credentials("host", "username", "password");
var filePaths = new List<string>();
// initializing filePaths ..
var tasks = filePaths
.Select(f => ParseRemoteFileAsync(credentials, f))
.ToArray();
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
// traverse through results..
}
public async Task<FileContent> ParseRemoteFileAsync(Credentials credentials, string filePath)
{
using (var sftp = new SftpClient(credentials.host, credentials.username, credentials.password))
{
sftp.Connect();
try
{
using (var remoteFileStream = sftp.OpenRead(filePath))
{
using (var reader = new StreamReader(remoteFileStream))
{
using (var csv = new CsvReader(reader))
{
/*
// Example of CSV parsing:
var records = new List<Foo>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = new Foo
{
Id = csv.GetField<int>("Id"),
Name = csv.GetField("Name")
};
records.Add(record);
}
*/
}
}
}
}
finally {
sftp.Disconnect();
}
}
}
/// <summary>
/// Implementation borrowed from [How to: Create an Object Pool by Using a
/// ConcurrentBag](https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-create-an-object-pool).
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObjectPool<T> : IDisposable
where T : IDisposable
{
private readonly Func<T> _objectGenerator;
private readonly ConcurrentBag<T> _objects;
public ObjectPool(Func<T> objectGenerator)
{
_objectGenerator = objectGenerator ?? throw new ArgumentNullException(nameof(objectGenerator));
_objects = new ConcurrentBag<T>();
}
public void Dispose()
{
while (_objects.TryTake(out var item))
{
item.Dispose();
}
}
public T GetObject()
{
return _objects.TryTake(out var item) ? item : _objectGenerator();
}
public void PutObject(T item)
{
_objects.Add(item);
}
}
internal class SftpclientTest
{
private readonly ObjectPool<SftpClient> _objectPool;
public SftpclientTest(Credentials credentials)
{
_objectPool = new ObjectPool<SftpClient>(() =>
{
var client = new SftpClient(credentials.host, credentials.username, credentials.password);
client.Connect();
return client;
});
}
public void GetDirectoryList()
{
var client = _objectPool.GetObject();
try
{
// client.ListDirectory() ..
}
finally
{
if (client.IsConnected)
{
_objectPool.PutObject(client);
}
}
}
public async Task ProcessRemoteFilesAsync()
{
var filePaths = new List<string>();
// initializing filePaths ..
var tasks = filePaths
.Select(f => ParseRemoteFileAsync(f))
.ToArray();
var results = await Task.WhenAll(tasks).ConfigureAwait(false);
// traverse through results..
}
public Task<FileContent> ParseRemoteFileAsync(string filePath)
{
var client = _objectPool.GetObject();
try
{
using (var remoteFileStream = client.OpenRead(filePath))
{
using (var reader = new StreamReader(remoteFileStream))
{
using (var csv = new CsvReader(reader))
{
// ..
}
}
return Task.FromResult(new FileContent());
}
}
finally
{
if (client.IsConnected)
{
_objectPool.PutObject(client);
}
}
}
}