用 FtpWebRequest
列出 FTP 目录内容最可靠
直接调用
FtpWebRequest是 C# 标准库中唯一原生支持 FTP 列目录的方式,
WebClient和
HttpClient都不支持
LIST或
MLSD命令。别被网上某些“一行代码搞定”的误导骗了——那些基本是伪造响应或依赖第三方库。
关键点在于:必须显式设置
Method为
"LIST"或
"MLSD",且不能用
GET;否则服务器返回 500 错误或空响应。
LIST兼容老服务器,但解析依赖服务端格式(Unix 风格 / DOS 风格),需自行正则提取文件名
MLSD是 RFC 3659 标准,返回结构化数据(含时间、权限、类型),推荐优先使用,但部分旧 FTP 服务器不支持 务必设置
Credentials,匿名访问需用
new NetworkCredential("", "")
超时建议设为 Timeout = 30000,FTP 控制连接容易卡住
MLSD
响应解析要注意字段顺序和编码
MLSD返回的是类似
modify=20231201102234;size=1024;type=file;name=report.pdf的键值对行,每行末尾有换行符,且可能含非 ASCII 字符(如中文文件名)。常见坑是直接
Encoding.UTF8读取导致乱码或截断。 先用
response.GetResponseStream()获取流,再用
StreamReader指定
Encoding.GetEncoding(ISO-8859-1)读取(FTP 协议规定 MLSD 元数据字段必须用 ISO-8859-1 编码) 文件名在
name=后,但可能含
=或分号,要用最后一个
name=截取,并去除前后空格和引号
type=file表示文件,
type=dir表示目录,别只靠扩展名判断
var request = (FtpWebRequest)WebRequest.Create("ftp://example.com/");
request.Method = "MLSD";
request.Credentials = new NetworkCredential("user", "pass");
using var response = (FtpWebResponse)request.GetResponse();
using var stream = response.GetResponseStream();
using var reader = new StreamReader(stream, Encoding.GetEncoding("ISO-8859-1"));
string line;
while ((line = reader.ReadLine()) != null) {
var nameMatch = Regex.Match(line, @"name=([^;]+)");
if (nameMatch.Success) {
string fileName = nameMatch.Groups[1].Value.Trim('"', ' ');
// 处理 fileName
}
}遇到 550 Access denied 或空列表先检查路径写法
FTP 路径不是 URL 路径:
ftp://host/a/b/中的
/a/b/是服务器上的绝对路径(从根目录起),而
ftp://host//a/b/(双斜杠)可能被某些服务器解释为用户主目录下的
a/b。列出根目录就用
ftp://host/,列子目录必须带尾部斜杠,否则部分服务器返回 550。 路径中避免反斜杠
\,全部用正斜杠
/用户名含
@符号时,URL 编码它(如
user%40domain),否则
WebRequest.Create解析失败 被动模式(PASV)是默认且必需的,若企业防火墙拦截 PASV 端口,需手动启用主动模式:
request.UsePassive = false,但极少需要 某些服务器要求先
CWD切换工作目录,再
LIST,此时得发两次请求
别忽略 FtpWebResponse.StatusCode
和异常细节
FTP 错误不都抛
WebException,有些静默返回空流或错误状态码。比如服务器返回
530 Not logged in时,
response.StatusCode是
FtpStatusCode.NotLoggedIn,但程序可能继续往下读空流,最终得到空列表。 始终检查
((FtpWebResponse)response).StatusCode是否为
FtpStatusCode.CommandOK或
FtpStatusCode.DataAlreadyOpen捕获
WebException后,读取
e.Response再转
FtpWebResponse,从中取
StatusCode和
StatusDescription调试时打印完整
StatusDescription,像
"550 Permission denied"比泛泛的异常消息有用得多
真实环境里,FTP 服务器五花八门,有的连
MLSD都没实现,有的返回 DOS 格式
LIST输出却声称支持
MLSD。先试
MLSD,失败再降级到
LIST+ 正则解析,比硬写一种方式靠谱得多。
