ASP.NET FTP下载触发150错误:传2-3个文件后异常,FileZila正常
解决ASP.NET FTP下载时“远程服务器返回错误: 150 Opening data channel for file download”异常
我来帮你分析并解决这个问题——你遇到的150错误其实不是服务器拒绝请求,而是客户端和服务器之间的数据通道在多次传输后出现了连接复用或资源泄漏的问题。毕竟FileZilla能正常传输所有文件,说明服务器配置本身是没问题的,问题出在你的ASP.NET代码的连接管理和资源释放逻辑上。
问题根源分析
你的代码里有几个关键的潜在问题:
- 连接复用导致资源耗尽:设置了
KeepAlive = true,这会让FTP控制连接保持打开状态,多次下载后可能导致服务器端的连接资源被占满,无法再建立新的数据通道。 - 资源未正确释放:手动调用
Close()在异常场景下可能不会执行,导致连接、流等资源泄漏,积累几次后就会触发错误。 - 传输模式未启用:注释掉了
UseBinary = true,文本模式可能会引发不必要的格式转换,增加传输异常的概率。 - 无限超时风险:
Timeout.Infinite可能让连接长时间挂起,占用服务器资源。
修改后的解决方案代码
下面是调整后的完整代码,我标注了关键修改点:
protected string TransferFile(string strPath, string strFtpPath, string strFtpUser, string strFtpPwd, string strSubClientFTPPath, string strSubClientFTPUser, string strSubClientFTPPwd, string strFileName, string strConvFileName, string strFileSize) { string strSuccess = ""; try { Double Size = 0; string strLocalFileSize = ""; // 用Path.Combine避免路径拼接错误 string localFilePath = Path.Combine(strPath, strConvFileName); if (File.Exists(localFilePath)) { FileInfo fileInfo = new FileInfo(localFilePath); double fileLength = Math.Round(fileInfo.Length); Size = fileLength > 0 && fileLength < 1024 ? 1 : Math.Round(fileLength / 1024 + 0.0001); strLocalFileSize = $"{Size}KB"; } if (!File.Exists(localFilePath) || strFileSize != strLocalFileSize) { // 确保Uri编码正确处理特殊字符 string ftpFileUri = new Uri(strFtpPath + strFileName.Replace("#", "%23")).ToString(); FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(ftpFileUri); // 跳过SSL证书验证(仅测试用,生产环境建议验证证书) System.Net.ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; reqFTP.EnableSsl = true; reqFTP.Timeout = 300000; // 设置5分钟超时,避免无限挂起 reqFTP.KeepAlive = false; // 关闭连接复用,每次下载后释放控制连接 reqFTP.Method = WebRequestMethods.Ftp.DownloadFile; reqFTP.UseBinary = true; // 启用二进制传输,避免文本格式转换问题 reqFTP.UsePassive = true; // 保持被动模式,和FileZilla默认行为一致 reqFTP.Credentials = new NetworkCredential(strFtpUser, strFtpPwd); // 使用using语句自动释放资源,即使异常也能确保关闭 using (FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse()) using (Stream ftpStream = response.GetResponseStream()) using (FileStream outputStream = new FileStream(localFilePath, FileMode.Create)) { int bufferSize = 2048; int readCount; byte[] buffer = new byte[bufferSize]; readCount = ftpStream.Read(buffer, 0, bufferSize); while (readCount > 0) { outputStream.Write(buffer, 0, readCount); readCount = ftpStream.Read(buffer, 0, bufferSize); } } strSuccess = ""; } else { strSuccess = "File Exists"; } } catch (Exception ex) { strSuccess = ex.Message; // 转义单引号,避免JavaScript语法错误 string escapedMessage = ex.Message.Trim().Replace("'", "\\'"); ClientScript.RegisterStartupScript(this.GetType(), "strScript", $"<script>alert('{escapedMessage}');</script>"); } return strSuccess; }
额外优化说明
- 路径拼接:用
Path.Combine替代手动拼接路径,避免不同系统下路径分隔符的问题。 - 错误提示转义:对异常信息里的单引号做转义,防止JavaScript弹窗出现语法错误。
- 被动模式验证:如果修改后还是有问题,可以尝试把
UsePassive改成false(主动模式),但需要确保服务器允许主动模式的端口连接(通常是20端口)。
内容的提问来源于stack exchange,提问作者Daya Ram
相关产品推荐
相关产品推荐

