FTP上传报“530 Not logged in”主因是未显式设置Credentials或未等待服务器就绪;大文件易OOM需流式上传;中文名乱码须按服务器编码适配;.NET Core+应迁移到FluentFTP。

FTP上传失败报“530 Not logged in”错误
多数情况不是密码错了,而是没显式调用 Login 或连接后立刻发命令,没等服务器就绪。C# 的 FtpWebRequest 默认不自动登录,必须手动设置凭据并确保 Credentials 在 GetRequestStream() 前已赋值。
常见踩坑点:
-
FtpWebRequest创建后直接GetResponse(),但没设Credentials→ 报 530 - 用户名/密码含特殊字符(如 @ / /),没做 URL 编码 → 登录被截断
- 使用被动模式(PASV)但防火墙阻断数据端口 → 表现为超时或 425 错误,但日志里先显示 530(因控制连接未真正建立)
实操建议:始终显式设置 Credentials,并用 UsePassive = true(默认值,但显式写出来更可控):
var req = (FtpWebRequest)WebRequest.Create("ftp://example.com/file.txt");
req.Credentials = new NetworkCredential("user", "pass");
req.UsePassive = true;
req.Method = WebRequestMethods.Ftp.UploadFile;
上传大文件时内存爆掉或超时
FtpWebRequest 默认把整个文件读进内存再发,遇到几百 MB 文件极易 OOM。根本原因在于它内部用了 GetRequestStream() 返回的流不支持分块写入缓冲,而开发者又习惯 File.ReadAllBytes() 一把塞进去。
正确做法是用流式上传,边读边写:
- 绝不用
File.ReadAllBytes()或File.ReadAllText() - 用
FileStream打开源文件,配合BufferedStream控制每次读取大小(如 8192 字节) - 务必在
using块里操作请求流,避免连接泄漏 - 设置
req.Timeout = 600000(10 分钟),否则默认 100 秒对大文件不够
示例关键片段:
using var fs = new FileStream("local.bin", FileMode.Open, FileAccess.Read, FileShare.Read, 8192, useAsync: true);
using var reqStream = req.GetRequestStream();
await fs.CopyToAsync(reqStream); // .NET 4.5+ 支持异步流复制
中文文件名上传后变成乱码或 550 错误
FTP 协议本身不定义文件名编码,服务器按自己理解解码。Windows IIS FTP 默认用 GB2312,Linux vsftpd 默认用 UTF-8,FtpWebRequest 却硬编码用系统 ANSI(即当前系统区域设置),导致跨平台上传中文名必然错乱。
没有通用解法,只能按目标服务器适配:
- vsftpd / pure-ftpd 等 Linux 服务 → 设
req.UseBinary = true并确保服务器配置utf8_enable=YES - IIS FTP → 必须用 GB2312 编码重命名远程路径,例如:
Uri.EscapeDataString("测试.txt", Encoding.GetEncoding("GB2312")) - 最稳妥方案:上传时用英文名,上传完立即发
WebRequestMethods.Ftp.Rename命令改名(需服务器支持)
注意:EscapeDataString 不等于 URL 编码全部字符,FTP 路径中斜杠 / 和冒号 : 不能被编码,否则路径解析失败。
.NET Core/.NET 5+ 中 FtpWebRequest 被标记为过时怎么办
微软从 .NET Core 2.1 开始就标记 FtpWebRequest 为 Obsolete,并在 .NET 6+ 中彻底禁用(抛 PlatformNotSupportedException)。这不是“还能用”,而是“随时会挂”。官方推荐用第三方库,但迁移不是简单换包。
现实选择只有两个:
- 继续用
FtpWebRequest→ 仅限 .NET Framework 项目;.NET 5+ 必须降级到System.Net.FtpClient(已归档)或FluentFTP - 迁移到
FluentFTP→ 它支持异步、UTF-8 文件名、SSL/TLS、代理,API 更直觉,但要注意:FtpClient实例不是线程安全的,别全局单例复用
最小迁移示例(FluentFTP):
using var client = new FtpClient("ftp.example.com", "user", "pass");
client.Connect();
client.UploadFile("local.txt", "/remote.txt");
复杂点在于:它默认启用 SSL,如果连的是普通 FTP(非 FTPS),得关掉 client.EncryptionMode = FtpEncryptionMode.None,否则连不上还报错很隐晦。










