使用SSH.NET异步下载SFTP文件转MemoryStream时无法返回结果
问题分析与解决方案
核心错误
你代码里的stream = (MemoryStream)result;是完全错误的——result是IAsyncResult类型的异步操作结果对象,根本不是MemoryStream。你一开始创建的stream已经被写入了下载的内容,不需要转换,只需要正确处理它。
另外你用了过时的APM(Begin/End)异步模式,SSH.NET已经提供了更贴合C# async/await语法的异步方法,没必要自己轮询等待。
修正后的代码
public class SshServices { public async Task<MemoryStream> DownloadFile(CancellationToken cancellationToken) { // 不要用静态的SftpClient,避免线程安全问题,方法内部创建并自动释放 using var sftpClient = new SftpClient("test.rebex.net", "demo", "password"); // 使用传入的取消令牌,而非固定的None await sftpClient.ConnectAsync(cancellationToken); if (!sftpClient.IsConnected) { throw new InvalidOperationException("无法连接到SFTP服务器"); } var serverFilePath = "/readme.txt"; // SFTP服务器路径标准用正斜杠 var stream = new MemoryStream(); // 使用官方异步下载方法,直接await等待完成 await sftpClient.DownloadFileAsync(serverFilePath, stream, cancellationToken); // 重置流的读取位置到开头,否则调用方读取时会从流末尾开始 stream.Position = 0; await sftpClient.DisconnectAsync(cancellationToken); return stream; } }
关键修改点
- 移除静态
s_sftpClient,改用using声明自动管理客户端资源,避免多线程环境下的安全问题 - 替换APM异步模式为
DownloadFileAsync,直接用await等待下载完成,无需手动轮询 - 下载完成后将
MemoryStream的Position设为0,确保调用方可以正常读取流内容 - 全程使用传入的
CancellationToken,支持操作取消 - 统一SFTP路径为正斜杠格式,适配绝大多数SFTP服务器的路径规范
内容的提问来源于stack exchange,提问作者Yasim Yasin
相关产品推荐
相关产品推荐

