最近通过FTP上传文件遇到了报“系统错误”异常。
一、代码示例
1、FTP上传代码
public Boolean ftpUpload(string filePath, string fileContent, string fileName, Boolean b_isFtpBackup)
{
b_ftpRet = true;
if (s_isFtp.Equals("1"))
{
FileUtil.getInstance().backupLog("ftpUpload start", IniUtil.getInstance().b_debug);
FileInfo fileInf = new FileInfo(filePath);
string uri = ftpURI + fileInf.Name;
FileUtil.getInstance().backupLog("根据uri创建FtpWebRequest对象" + uri, IniUtil.getInstance().b_debug);
// 根据uri创建FtpWebRequest对象
FtpWebRequest reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(uri));
if (ftpUserID != null)
{
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
}
FileUtil.getInstance().backupLog("根据uri创建FtpWebRequest对象完毕", IniUtil.getInstance().b_debug);
// 默认为true,连接不会被关闭
// 在一个命令之后被执行
reqFTP.KeepAlive = false;
//指定执行什么命令
reqFTP.Method = WebRequestMethods.Ftp.UploadFile;
// 指定数据传输类型
reqFTP.UseBinary = true;
reqFTP.UsePassive = false;
// 上传文件时通知服务器文件的大小
reqFTP.ContentLength = fileInf.Length;
reqFTP.Timeout = 30000;
// 缓冲大小设置为2kb
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
FileStream fs = null;
Stream strm = null;
try
{
FileUtil.getInstance().backupLog("111111111111111", IniUtil.getInstance().b_debug);
// 打开一个文件流 (System.IO.FileStream) 去读上传的文件
fs = fileInf.OpenRead();
FileUtil.getInstance().backupLog("22222222222", IniUtil.getInstance().b_debug);
// 把上传的文件写入流
strm = reqFTP.GetRequestStream();
FileUtil.getInstance().backupLog("3333333333333", IniUtil.getInstance().b_debug);
// 每次读文件流的2kb
contentLen = fs.Read(buff, 0, buffLength);
FileUtil.getInstance().backupLog("4444444444444444", IniUtil.getInstance().b_debug);
// 流内容没有结束
while (contentLen != 0)
{
// 把内容从file stream 写入 upload stream
strm.Write(buff, 0, contentLen);
FileUtil.getInstance().backupLog("555555555555555", IniUtil.getInstance().b_debug);
contentLen = fs.Read(buff, 0, buffLength);
}
if (b_isFtpBackup)
{
FileUtil.instance.Log(filePath + "--上传成功!");
}
else
{
if (MainForm.instance.bufferFTPBtn.Enabled)
{
FTPStateChange("缓存文件上传中...");
FileUtil.getInstance().backupLog("缓存文件上传中...");
BufferFTPChange(false);
}
}
}
catch (Exception ex)
{
if (ftpBufferUploadRuuning)
{
FTPStateChange("FTP连接失败!");
FileUtil.getInstance().backupLog("FTP连接失败!");
BufferFTPChange(true);
}
b_ftpRet = false;
if (b_isFtpBackup)
{
FileUtil.instance.Log(filePath + "--上传失败!");
}
FileUtil.instance.backupLog("Ftphelper Upload Error --> " + ex.Message);
if (IniUtil.getInstance().isBackup.Equals("1") && b_isFtpBackup)
{
FileUtil.getInstance().WriteFile(FileUtil.getInstance().ftpBufferPath + "\\" + fileName, fileContent, null, FileMode.Create);
}
}
finally
{
// 关闭两个流
try
{
if (strm != null)
{
strm.Close();
}
}
catch (Exception e)
{
FileUtil.instance.backupLog("strm.Close() exception," + e.Message);
}
try
{
if (fs != null)
{
fs.Close();
}
}
catch (Exception e)
{
FileUtil.instance.backupLog("fs.Close() exception," + e.Message);
}
}
}
return b_ftpRet;
}
}
2、检测FTP是否可用代码
public Boolean CheckFtp()
{
Boolean ret = true;
try {
FileUtil.getInstance().Log("checkftp start," + ftpURI);
FtpWebRequest reqFTP;
// 根据uri创建FtpWebRequest对象
FileUtil.getInstance().Log("根据uri创建FtpWebRequest对象...");
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI));
FileUtil.getInstance().Log("获取ListDirectory...");
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
FileUtil.getInstance().Log("userID,password登录...");
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
FileUtil.getInstance().Log("登录后等待response...");
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
FileUtil.getInstance().Log("获取response成功,ftp连接成功!");
} catch (Exception e) {
if(!b_ftpRet){
FileUtil.instance.Log("CheckFtp Exception:" + e.Message);
}
ret = false;
}
return ret;
}
测试FTP连接居然用了10秒,而我的FTPtimeout设置的是2秒,故失败,但是为什么这么慢呢?
3、FTP工具测试
二、原因分析
1、FtpWebRequest.KeepAlive
如果不应销毁到服务器的连接,则为 true
;否则为 false
。 默认值是 true
。
2、FtpWebRequest.UseBinary
true
,指示服务器要传输的是二进制数据;false
,指示数据为文本。 默认值是 true
。
3、FtpWebRequest.UsePassive
(1)默认值:true,被动模式
PASV(被动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路。 当需要传送数据时, 服务器在命令链路上用PASV命令告诉客户端:“我打开了***X端口,你过来连接我”。于是客户端向服务器的***X端口发送连接请求,建立一条数据链 路来传送数据。
(2)UsePassive=false,主动模式
PORT(主动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路。 当需要传送数据时, 客户端在命令链路上用PORT命令告诉服务器:“我打开了***X端口,你过来连接我”。于是服务器从20端口向客户端的***X端口发送连接请求,建立 一条数据链路来传送数据。
(3)优缺点
主动FTP对FTP服务器的管理有利,但对客户端的管理不利。因为FTP服务器企图与客户端的高位随机端口建立连接,而这个端口很有可能被客户端的防火墙阻塞掉。被动FTP对FTP客户端的管理有利,但对服务器端的管理不利。因为客户端要与服务器端建立两个连接,其中一个连到一个高位随机端口,而这个端口很有可能被服务器端的防火墙阻塞掉。
感觉和这个没啥关系啊。
4、此时,灵光乍现,reqFTP.Timeout = 2000;
FTP上传超时2秒,也就是说2秒上传失败的话,就不传了,但是测试FTP都需要10秒,故失败,系统错误,传不上去。
索性改为20秒算了,开心的是性能客户不考虑,告诉我能传上去就行了,真的是良心客户啊,但是还是没找到根本原因,我感觉是FTP服务端设置了什么东西,不让客户端传的太频繁,防止服务端由于客户端太多,上传太频繁而宕机。
由于工作繁忙,先到这,以后出现问题,再重构吧。