using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace FirstSolver
{
/// <summary>
/// FTP 助手
/// </summary>
public static class FtpHelper
{
/// <summary>
/// 文件上传重试次数
/// </summary>
public static int UploadRetry { get; set; } = 2;
/// <summary>
/// 判断FTP目录是否存在
/// </summary>
/// <param name="requestUriString">目录路径,注意结尾必须带“/”</param>
/// <returns>true/false</returns>
public static bool DirectoryExists(string requestUriString)
{
WebRequest request = WebRequest.Create(requestUriString.EndsWith("/") ? requestUriString : $"{requestUriString}/");
request.Method = WebRequestMethods.Ftp.ListDirectory;
using (var response = (FtpWebResponse)request.GetResponse())
{
return response.StatusCode == FtpStatusCode.DataAlreadyOpen;
}
}
/// <summary>
/// 判断FTP文件是否存在
/// </summary>
/// <param name="requestUriString">文件路径</param>
/// <returns>true/false</returns>
public static bool FileExists(string requestUriString)
{
WebRequest request = WebRequest.Create(requestUriString);
request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
using (var response = (FtpWebResponse)request.GetResponse())
{
return response.StatusCode == FtpStatusCode.FileStatus;
}
}
/// <summary>
/// 获取文件最后修改时间
/// </summary>
/// <param name="requestUriString">文件路径</param>
/// <returns>文件最后修改时间</returns>
/// <remarks>也可以用于判断文件是否存在</remarks>
public static DateTime GetDateTimestamp(string requestUriString)
{
WebRequest request = WebRequest.Create(requestUriString);
request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
using (var response = (FtpWebResponse)request.GetResponse())
{
return response.LastModified;
}
}
/// <summary>
/// 在FTP服务器上创建目录
/// </summary>
/// <param name="requestUriString">目录路径,结尾可以不带“/”</param>
/// <returns>true/false</returns>
/// <remarks>要求上级目录必须存在,否则创建失败</remarks>
public static bool MakeDirectory(string requestUriString)
{
WebRequest request = WebRequest.Create(requestUriString);
request.Method = WebRequestMethods.Ftp.MakeDirectory;
using (var response = (FtpWebResponse)request.GetResponse())
{
return response.StatusCode == FtpStatusCode.PathnameCreated;
}
}
/// <summary>
/// 在FTP服务器上创建目录
/// </summary>
/// <param name="requestUriString">目录路径,结尾可以不带“/”</param>
/// <returns>true/false</returns>
/// <remarks>如果上级目录不存在,将依次创建</remarks>
public static bool MakeDirectoryRecursive(string requestUriString)
{
string[] parts = requestUriString.Split('/', StringSplitOptions.RemoveEmptyEntries);
StringBuilder sb = new StringBuilder();
sb.Append($"{parts[0]}//{parts[1]}/");
for (int i = 2; i < parts.Length - 1; i++)
{
sb.Append($"{parts[i]}/");
MakeDirectory(sb.ToString());
}
return MakeDirectory(requestUriString);
}
/// <summary>
/// 上传文件
/// </summary>
/// <param name="requestUriString">上传路径</param>
/// <param name="data">文件内容</param>
/// <returns>true/false</returns>
public static bool Upload(string requestUriString, byte[] data)
{
using (var client = new WebClient())
{
for (int i = 0; i < UploadRetry; i++)
{
try
{
client.UploadData(requestUriString, data);
return true;
}
catch
{ // [失败的原因很可能是目录未创建]错误类型:(553) | (550)
MakeDirectoryRecursive(requestUriString.Substring(0, requestUriString.LastIndexOf("/") + 1));
}
}
}
return false;
}
/// <summary>
/// 上传文件
/// </summary>
/// <param name="requestUriString">上传路径</param>
/// <param name="localFilename">要上传的文件名称</param>
/// <returns>true/false</returns>
public static bool Upload(string requestUriString, string localFilename)
{
return Upload(requestUriString, File.ReadAllBytes(localFilename));
}
/// <summary>
/// 异步上传文件
/// </summary>
/// <param name="requestUriString">上传路径</param>
/// <param name="data">文件内容</param>
/// <returns>true/false</returns>
public async static Task<bool> UploadAsync(string requestUriString, byte[] data)
{
return await Task.Run(() =>
{
return Upload(requestUriString, data);
});
}
/// <summary>
/// 异步上传文件
/// </summary>
/// <param name="requestUriString">上传路径</param>
/// <param name="localFilename">要上传的文件名称</param>
/// <returns>true/false</returns>
public async static Task<bool> UploadAsync(string requestUriString, string localFilename)
{
return await Task.Run(() =>
{
return Upload(requestUriString, localFilename);
});
}
/// <summary>
/// 读取FTP文件内容
/// </summary>
/// <param name="requestUriString">要下载的文件路径</param>
/// <returns>存储文件内容的字节数组</returns>
public static byte[] ReadFile(string requestUriString)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.DownloadFile;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
using (Stream ftpStream = response.GetResponseStream())
{
using (MemoryStream stream = new MemoryStream())
{
ftpStream.CopyTo(stream);
return stream.ToArray();
}
}
}
}
/// <summary>
/// 下载文件
/// </summary>
/// <param name="requestUriString">要下载的文件路径</param>
/// <param name="localFilename">文件保存路径</param>
public static void Download(string requestUriString, string localFilename)
{
string? folder = Path.GetDirectoryName(localFilename);
if (!Directory.Exists(folder)) Directory.CreateDirectory(folder!);
File.WriteAllBytes(localFilename, ReadFile(requestUriString));
}
/// <summary>
/// 获取文件大小
/// </summary>
/// <param name="requestUriString">文件路径</param>
/// <param name="force">是否通过下载方式强制获取文件大小,默认为false</param>
/// <returns>文件大小</returns>
/// <remarks>FTP服务器可能不支持GetFileSize操作,但支持DownloadFile操作</remarks>
public static long GetFileSize(string requestUriString, bool force = false)
{
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.GetFileSize;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
return response.ContentLength;
}
}
catch
{ // FTP服务器不支持GetFileSize操作
if (!force) throw;
// 从文件的详细列表中获取文件大小
string filename = Path.GetFileName(requestUriString);
foreach (var item in ListDirectoryDetails(requestUriString.Substring(0, requestUriString.Length - filename.Length)))
{
if (item.StartsWith("-") && item.EndsWith($" {filename}"))
{ // 倒数第5项为文件大小
return int.Parse(item.Split(' ', StringSplitOptions.RemoveEmptyEntries)[^5]);
}
}
return -1;
}
}
/// <summary>
/// 重命名文件
/// </summary>
/// <param name="requestUriString">要重命名的文件路径</param>
/// <param name="newFileName">新文件名</param>
/// <returns>true/false</returns>
public static bool Rename(string requestUriString, string newFileName)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.Rename;
request.RenameTo = newFileName;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
return response.StatusCode == FtpStatusCode.FileActionOK;
}
}
/// <summary>
/// 获取 FTP 服务器上的文件的简短列表
/// </summary>
/// <param name="requestUriString">要获取文件列表的目录路径</param>
/// <returns>文件的简短列表</returns>
/// <remarks>注意:列表里也包含子目录</remarks>
public static IEnumerable<string> ListDirectory(string requestUriString)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.ListDirectory;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default))
{
while (!reader.EndOfStream)
{
yield return reader.ReadLine();
}
}
}
}
/// <summary>
/// 获取 FTP 服务器上的文件的详细列表
/// </summary>
/// <param name="requestUriString">要获取文件列表的目录路径</param>
/// <returns>文件的详细列表</returns>
/// <remarks>注意:列表里也包含子目录</remarks>
public static IEnumerable<string> ListDirectoryDetails(string requestUriString)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default))
{
while (!reader.EndOfStream)
{
yield return reader.ReadLine();
}
}
}
}
/// <summary>
/// 获取FTP服务器当前路径
/// </summary>
/// <param name="requestUriString">文件路径</param>
/// <returns>当前路径</returns>
public static string PrintWorkingDirectory(string requestUriString)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.PrintWorkingDirectory;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
return response.StatusDescription.Split('\"', StringSplitOptions.RemoveEmptyEntries)[1];
}
}
/// <summary>
/// 删除文件
/// </summary>
/// <param name="requestUriString">要删除的文件路径</param>
/// <returns>true/false</returns>
/// <remarks>如果文件不存在将引发异常</remarks>
public static bool DeleteFile(string requestUriString)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.DeleteFile;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
return response.StatusCode == FtpStatusCode.FileActionOK;
}
}
/// <summary>
/// 追加数据
/// </summary>
/// <param name="requestUriString">文件路径</param>
/// <param name="data">追加内容</param>
/// <returns>true/false</returns>
public static bool AppendFile(string requestUriString, byte[] data)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.AppendFile;
request.ContentLength = data.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
}
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
return response.StatusCode == FtpStatusCode.FileActionOK;
}
}
/// <summary>
/// 删除目录
/// </summary>
/// <param name="requestUriString">要删除的目录路径</param>
/// <returns>true/false</returns>
/// <remarks>如果目录不为空,则删除失败</remarks>
public static bool RemoveDirectory(string requestUriString)
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(new Uri(requestUriString));
request.Method = WebRequestMethods.Ftp.RemoveDirectory;
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
{
return response.StatusCode == FtpStatusCode.FileActionOK;
}
}
/// <summary>
/// 迭代删除目录
/// </summary>
/// <param name="requestUriString">要删除的目录路径</param>
/// <returns>true/false</returns>
public static bool RemoveDirectoryRecursive(string requestUriString)
{
if (!requestUriString.EndsWith("/")) requestUriString = $"{requestUriString}/";
foreach (var item in ListDirectoryDetails(requestUriString))
{
if (item.StartsWith("d"))
{ // 倒数第一项为目录名称
if (!RemoveDirectoryRecursive($"{requestUriString}{item.Split(' ', StringSplitOptions.RemoveEmptyEntries)[^1]}/"))
{
return false;
}
}
else if (item.StartsWith("-"))
{ // 倒数第一项为文件名称
if (!DeleteFile($"{requestUriString}{item.Split(' ', StringSplitOptions.RemoveEmptyEntries)[^1]}"))
{
return false;
}
}
}
return RemoveDirectory(requestUriString);
}
}
}