SSH.NET上传失败主因是服务端SFTP子系统未启用、认证凭据缺失(如ConnectionInfo未传Password/PrivateKey或私钥未Load口令)、远程路径非绝对路径或越界(如chroot限制下未用"/"开头);大文件须用Stream流式上传防内存溢出;验证SFTP是否真可用应通过ssh -v查看日志中是否有“Sending subsystem: sftp”。

用 SSH.NET 上传文件失败,八成不是代码写错,而是服务端没开 SFTP 子系统、认证方式配错,或路径越界。连得上 SSH 不代表能用 SFTP。
ConnectionInfo 构造时漏传凭据,会静默失败
SSH.NET 的 SftpClient 在构造时若传入的 ConnectionInfo 没包含有效认证(比如没设 PasswordConnectionInfo 或没加载私钥),它不会报错,而是 fallback 到无认证模式——服务器直接拒连,抛出 AuthenticationException 或 SocketException。
- 必须显式创建带凭据的连接对象:用
PasswordConnectionInfo(用户名+密码)或PrivateKeyConnectionInfo(用户名+PrivateKeyFile) - 私钥带口令时,务必调用
privateKey.Load(<code>passphrase</code>),否则加载失败但不提示 - 别依赖默认值:即使端口是 22,也建议显式传
port: 22,某些定制 SSH 服务跑在非标端口
UploadFile 报 “No such file” 或权限拒绝
错误常出现在远程路径处理上。SFTP 服务端(尤其启用了 ForceCommand internal-sftp + ChrootDirectory)只允许访问受限根目录下的绝对路径,且不支持 ~/ 或相对路径展开。
- 远程路径必须是绝对路径,如
"/upload/test.zip",不能写"upload/test.zip" - 先用
sftpClient.ListDirectory(".")确认当前工作目录是否是你预期的根目录 - 若需创建多级目录,不能靠
UploadFile自动建,得手动遍历调用CreateDirectory,注意顺序从根到叶子 - 上传前检查目标父目录是否存在且可写:
sftpClient.Exists("/upload") && sftpClient.CanWrite("/upload")
大文件上传卡死或内存暴涨
SSH.NET 的 UploadFile(string, string) 重载内部会调用 File.ReadAllBytes(),对 >50MB 的文件极易触发 GC 压力或 OutOfMemoryException,尤其在 32 位进程或低内存容器中。
- 改用流式上传:用
UploadFile(Stream, string),自己控制FileStream的缓冲区大小和生命周期 - 示例:
using var fs = new FileStream(@"C:\data\big.bin", FileMode.Open, FileAccess.Read, FileShare.Read, 8192, useAsync: true); sftpClient.UploadFile(fs, "/remote/big.bin");
- 避免在
using块里包裹FileStream后再传给UploadFile——流可能提前被关掉,导致上传中断 - 如需监控进度,继承
Stream写个带回调的包装类,别用Progress<T>包整个上传过程
怎么确认服务端真支持 SFTP?
别只信文档或运维口头说“开了 SFTP”。终端执行 ssh -v user@host 是最准的验证方式:看到日志里有 debug1: Sending subsystem: sftp 才算真正可用;若只有 debug1: Authentication succeeded 但后续没 sftp 协商,说明服务端只开了 shell,没启用 SFTP 子系统。
此时代码无论怎么调 SftpClient 都会卡在 Connect() 或抛 SshConnectionException。修复只能找运维:检查 /etc/ssh/sshd_config 是否含 Subsystem sftp /usr/lib/openssh/sftp-server 或 internal-sftp,且未被注释。


















