许多博客提供的ftp工具类中,方法被调时需要先移动ftp服务器当前所在目录,并且存在bug,非常不爽。本人加以改动,符合调用习惯,并经过实际项目测试。
FTP工具类代码
/// <summary>
/// Ftp工具类
/// </summary>
public class FtpHelper
{
private string ftpHostIP;
private string username;
private string password;
/// <summary>
/// 服务器用了啥ftp软件
/// </summary>
public FtpServerApp FtpApp { get; set; } = FtpServerApp.Unknown;
/// <summary>
/// 服务器根目录
/// </summary>
private string ftpURI
{
get { return "ftp://" + ftpHostIP + "/"; }
}
/// <summary>
/// 初始化ftp参数
/// </summary>
/// <param name="ftpHostIP">ftp主机IP</param>
/// <param name="username">ftp账户</param>
/// <param name="password">ftp密码</param>
public FtpHelper(string ftpHostIP, string username, string password)
{
this.ftpHostIP = ftpHostIP;
this.username = username;
this.password = password;
}
/// <summary>
/// 测试连接
/// </summary>
public bool checkConnect()
{
try
{
//TcpClient m_connection = new TcpClient();
//m_connection.Connect(ftpHostIP, 21);
FtpWebRequest ftp = GetRequest(ftpURI);
ftp.Method = WebRequestMethods.Ftp.ListDirectory;
ftp.Timeout = 3000;
FtpWebResponse ftpResponse = (FtpWebResponse)ftp.GetResponse();
ftpResponse.Close();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// 异常方法委托,通过Lamda委托统一处理异常,方便改写
/// </summary>
/// <param name="method">当前执行的方法</param>
/// <param name="action"></param>
/// <returns></returns>
private bool MethodInvoke(string method, Action action)
{
if (action != null)
{
try
{
action();
Console.WriteLine($@"FtpHelper.{method}:执行成功");
return true;
}
catch (Exception ex)
{
Console.WriteLine($@"FtpHelper({ftpHostIP},{username},{password}).{method}:执行失败:\n {ex}");
return false;
}
}
else
{
return false;
}
}
/// <summary>
/// 异常方法委托,通过Lamda委托统一处理异常,方便改写
/// </summary>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="method"></param>
/// <param name="func"></param>
/// <returns></returns>
private T MethodInvoke<T>(string method, Func<T> func)
{
if (func != null)
{
try
{
Console.WriteLine($@"FtpHelper({ftpHostIP},{username},{password}).{method}:执行成功");
return func();
}
catch (Exception ex)
{
Console.WriteLine($@"FtpHelper({ftpHostIP},{username},{password}).{method}:执行失败 \r\n{ex}");
return default(T);
}
}
else
{
return default(T);
}
}
//根据服务器信息FtpWebRequest创建类的对象
private FtpWebRequest GetRequest(string URI)
{
FtpWebRequest result = (FtpWebRequest)WebRequest.Create(new Uri(URI));
result.Credentials = new NetworkCredential(username, password);
result.KeepAlive = true;
//result.UsePassive = false;
//result.UseBinary = false;
return result;
}
/// <summary> 上传文件</summary>
/// <param name="localFilePath">需要上传的文件全路径</param>
/// <param name="remoteDir">需要上传到的目标目录下</param>
public bool UploadFile(string localFilePath, string remoteDir = "")
{
FileInfo fileInfo = new FileInfo(localFilePath);
if (remoteDir != "") CreateDir(remoteDir);//检查文件目录,不存在就自动创建
string uri = Path.Combine(ftpURI, remoteDir, fileInfo.Name);
return MethodInvoke($@"uploadFile({localFilePath},{remoteDir})", () =>
{
FtpWebRequest ftp = GetRequest(uri);
ftp.Method = WebRequestMethods.Ftp.UploadFile;
ftp.ContentLength = fileInfo.Length;
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;
using (FileStream fs = fileInfo.OpenRead())
{
using (Stream strm = ftp.GetRequestStream())
{
contentLen = fs.Read(buff, 0, buffLength);
while (contentLen != 0)
{
strm.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}
strm.Close();
}
fs.Close();
}
});
}
/// <summary>
/// 把一个目录上传到另一目录下
/// </summary>
/// <param name="localDir">源目录</param>
/// <param name="remoteDirName">目标目录</param>
public void UploadDirectory(string localDir, string remoteDir = "")
{
string localDirName = string.Empty;
int targIndex = localDir.LastIndexOf("\\");
//if (targIndex > -1 && targIndex != (localDir.IndexOf(":\\") + 1))
// localDirName = localDir.Substring(0, targIndex);
localDirName = localDir.Substring(targIndex + 1);
//复制到的ftp服务器具体路径
string newDir = remoteDir+"/"+ localDirName;
//E:\504\UATS\UATS\bin\Debug\Library
MethodInvoke($@"UploadAllFile({localDir},{remoteDir})", () =>
{
CreateDir(newDir);
DirectoryInfo directoryInfo = new DirectoryInfo(localDir);
FileInfo[] files = directoryInfo.GetFiles();
//复制所有文件
foreach (FileInfo file in files)
{
UploadFile(file.FullName, newDir);
}
//最后复制目录
DirectoryInfo[] directoryInfoArray = directoryInfo.GetDirectories();
foreach (DirectoryInfo dir in directoryInfoArray)
{
UploadDirectory(Path.Combine(localDir, dir.Name), newDir);
}
});
}
/// <summary>
/// 删除单个文件
/// </summary>
/// <param name="remoteFilePath"></param>
public bool DeleteFile(string remoteFilePath)
{
string uri = Path.Combine(ftpURI, remoteFilePath);
return MethodInvoke($@"DelFile({remoteFilePath})", () =>
{
FtpWebRequest ftp = GetRequest(uri);
ftp.Method = WebRequestMethods.Ftp.DeleteFile;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
response.Close();
});
}
/// <summary>
/// 删除最末及空目录
/// </summary>
/// <param name="remoteDir"></param>
public bool DeleteDir(string remoteDir)
{
string uri = Path.Combine(ftpURI, remoteDir);
return MethodInvoke($@"DelDir({remoteDir})", () =>
{
FtpWebRequest ftp = GetRequest(uri);
ftp.Method = WebRequestMethods.Ftp.RemoveDirectory;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
response.Close();
});
}
/// <summary> 删除目录或者其目录下所有的文件 </summary>
/// <param name="remoteDir">目录名称</param>
public void DeleteDirAll(string remoteDir)
{
MethodInvoke($@"DeleteAll({remoteDir})", () =>
{
List<ActFile> files = GetRemoteFiles(remoteDir);
foreach(var f in files)
{
if(f.isDir)
{
DeleteDirAll(remoteDir + "/" + f.name);
DeleteDir(remoteDir + "/" + f.name);
DeleteDir(remoteDir);
}
else
{
DeleteFile(remoteDir + "/" + f.name);
}
}
}
);
}
/// <summary>
/// 下载单个文件
/// </summary>
/// <param name="remoteFilePath">从ftp要下载的文件全路径</param>
/// <param name="localDir">下载至的本地路径</param>
public bool DownloadFile(string remoteFilePath, string localDir)
{
string filename = remoteFilePath.Substring(remoteFilePath.LastIndexOf("/") + 1);
string tmpname = Guid.NewGuid().ToString();
string uri = Path.Combine(ftpURI, remoteFilePath);
return MethodInvoke($@"DownloadFile({remoteFilePath},{localDir},{filename})", () =>
{
if (!Directory.Exists(localDir)) Directory.CreateDirectory(localDir);
FtpWebRequest ftp = GetRequest(uri);
ftp.Method = WebRequestMethods.Ftp.DownloadFile;
using (FtpWebResponse response = (FtpWebResponse)ftp.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (FileStream fs = new FileStream(Path.Combine(localDir, filename), FileMode.CreateNew))
{
byte[] buffer = new byte[2048];
int read = 0;
do
{
read = responseStream.Read(buffer, 0, buffer.Length);
fs.Write(buffer, 0, read);
} while (!(read == 0));
responseStream.Close();
fs.Flush();
fs.Close();
}
responseStream.Close();
}
response.Close();
}
});
}
/// <summary>
/// 从FTP把某目录下的所有内容(递归)下载至本地某目录
/// </summary>
/// <param name="remoteDir">FTP文件夹路径</param>
/// <param name="localDir">下载到的的本地文件夹路径</param>
public void DownloadAllFile(string remoteDir, string localDir)
{
MethodInvoke($@"DownloadAllFile({remoteDir},{localDir})", () =>
{
List<ActFile> files = GetRemoteFiles(remoteDir);
if (!Directory.Exists(localDir))
{
Directory.CreateDirectory(localDir);
}
foreach (var f in files)
{
if (f.isDir) //文件夹,递归查询
{
DownloadAllFile(remoteDir +"/"+f.name, Path.Combine(localDir, f.name));
}
else //文件,直接下载
{
DownloadFile(remoteDir + "/" + f.name, localDir);
}
}
});
}
/// <summary>
/// 获取当前目录下的目录及文件
/// </summary>
public List<ActFile> GetRemoteFiles(string remoteDir)
{
var ftpfileList = new List<ActFile>();
string uri = Path.Combine(ftpURI, remoteDir);
return MethodInvoke($@"GetFtpFile({remoteDir})", () =>
{
var a = new List<List<string>>();
//FtpWebRequest ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI));
FtpWebRequest ftp = GetRequest(uri);
ftp.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
Stream stream = ftp.GetResponse().GetResponseStream();
using (StreamReader sr = new StreamReader(stream))
{
string line = sr.ReadLine();
while (!string.IsNullOrEmpty(line))
{
switch (FtpApp)
{
case FtpServerApp.FileZilla:
ftpfileList.Add(new ActFile
{
isDir = line.StartsWith("d"),
name = line.Substring(49).Trim(),
path = remoteDir
});
break;
case FtpServerApp.IIS:
ftpfileList.Add(new ActFile
{
isDir = line.IndexOf("<DIR>") > -1,
name = line.Substring(39).Trim(),
path = Path.Combine(remoteDir, line.Substring(39).Trim())
});
break;
case FtpServerApp.Unknown:
default:
break;
}
line = sr.ReadLine();
}
sr.Close();
}
return ftpfileList;
});
}
/// <summary>
/// 获取FTP目录下的所有目录及文件包括其子目录和子文件
/// </summary>
/// param name="result"></param>
/// <param name="remoteDir"></param>
/// <returns></returns>
public List<ActFile> GetAllRemoteFile(List<ActFile> result, string remoteDir, int level = 0)
{
var ftpfileList = new List<ActFile>();
string uri = Path.Combine(ftpURI, remoteDir);
return MethodInvoke($@"GetAllFtpFile({remoteDir})", () =>
{
ftpfileList = GetRemoteFiles(remoteDir);
result.AddRange(ftpfileList);
var newlist = ftpfileList.Where(x => x.isDir).ToList();
foreach (var item in newlist)
{
GetAllRemoteFile(result, item.path, level + 1);
}
return result;
});
}
/// <summary>
/// 检查目录是否存在
/// </summary>
/// <param name="dirName"></param>
/// <param name="remoteParentDir"></param>
/// <returns></returns>
public bool ExistDir(string dirName, string remoteParentDir = "")
{
string uri = ftpURI+remoteParentDir;
return MethodInvoke($@"CheckDir({dirName}{remoteParentDir})", () =>
{
FtpWebRequest ftp = GetRequest(uri);
ftp.UseBinary = true;
ftp.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
WebResponse response = ftp.GetResponse();
//Stream stream = ftp.GetResponse().GetResponseStream();
Stream stream =response.GetResponseStream();
using (StreamReader sr = new StreamReader(stream))
{
string line = sr.ReadLine();
while (!string.IsNullOrEmpty(line))
{
switch (FtpApp)
{
case FtpServerApp.FileZilla:
if (line.StartsWith("d"))
{
if (line.Substring(49).Trim() == dirName)
return true;
}
break;
case FtpServerApp.IIS:
if (line.IndexOf("<DIR>") > -1)
{
if (line.Substring(39).Trim() == dirName)
return true;
}
break;
case FtpServerApp.Unknown:
default:
break;
}
line = sr.ReadLine();
}
sr.Close();
}
stream.Close();
return false;
});
}
/// <summary>
/// 在ftp服务器上创建指定目录,父目录不存在则创建
/// </summary>
/// <param name="remoteDir">要创建的指定目录</param>
/// <returns></returns>
public bool CreateDir(string remoteDir)
{
var dirs = remoteDir.Split('/').ToList();//针对多级目录分割
string currentDir = string.Empty;
return MethodInvoke($@"MakeDir({remoteDir})", () =>
{
foreach (var dir in dirs)
{
if (!ExistDir(dir, currentDir))//检查目录不存在则创建
{
currentDir += (currentDir.EndsWith("/")? "": "/") + dir;
string uri = ftpURI+currentDir;
FtpWebRequest ftp = GetRequest(uri);
ftp.Method = WebRequestMethods.Ftp.MakeDirectory;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
response.Close();
}
else
{
currentDir += (currentDir.EndsWith("/") ? "" : "/") + dir;
}
}
});
}
/// <summary>文件重命名 </summary>
/// <param name="currentFilename">当前名称</param>
/// <param name="newFilename">重命名名称</param>
/// <param name="remoteDir">所在的目录</param>
public bool Rename(string currentFilename, string newFilename, string remoteDir = "")
{
string uri = Path.Combine(ftpURI, remoteDir, currentFilename);
return MethodInvoke($@"Rename({currentFilename},{newFilename},{remoteDir})", () =>
{
FtpWebRequest ftp = GetRequest(uri);
ftp.Method = WebRequestMethods.Ftp.Rename;
ftp.RenameTo = newFilename;
FtpWebResponse response = (FtpWebResponse)ftp.GetResponse();
response.Close();
});
}
}
/// <summary>
/// ftp服务器软件
/// </summary>
public enum FtpServerApp
{
Unknown = 0,
IIS = 1,
FileZilla = 2
}
/// <summary>
/// 文件(夹)信息
/// </summary>
public class ActFile
{
public int level { get; set; } = 0;
//真则是文件夹(否则为文件)
public bool isDir { get; set; }
public string name { get; set; }
public string path
{
get; set;
}
}
调用
public static FtpHelper CreateFtpHelper()
{
try
{
IPAddress ip;
if (!IPAddress.TryParse(FtpServerIP, out ip))
{
throw new Exception("未能建立与ftp服务器的连接,未设置合法的ftp服务器IP地址。");
}
FtpHelper ftpHelper = new FtpHelper(FtpServerIP, FtpUserID, FtpPassword);
if (ftpHelper.checkConnect())
{
ftpHelper.FtpApp = (FtpServerApp)Enum.Parse(typeof(FtpServerApp), FtpServerApp);
return ftpHelper;
}
throw new Exception("未能建立与ftp服务器的连接,可能是由于服务器端口21未打开或被占用。");
}
catch(Exception ex)
{
throw ex;
}
}
//然后就可以使用这个FtpHelper对象调不同方法了(●'◡'●)
备注
1.本地路径拼接使用反斜杠,但考虑转义字符的影响,代码中使用"\"或@“\”或Path.Combine(localDir, f.name);
2.服务器路径拼接使用斜杠即可“/”;
3.上述代码支持ftpServer软件:windows自带IIS或FileZilla,本人推荐后者,教程转至使用Filezilla Server配置FTP服务器,配置时注意勾选权限。