【C#】FTP文件传输
一、Windows系统搭建FTP服务器
参考教程:https://baijiahao.baidu.com/s?id=1722628091716283305&wfr=spider&for=pc
Win11家庭版:https://blog.csdn.net/zoe757081803/article/details/125164066
Win11添加用户账户:https://product.pconline.com.cn/itbk/software/dnyw/1524/15244261.html
二、例程
public class FtpHelper { #region 字段 string ftpURI; string ftpUserID; string ftpServerIP; string ftpPassword; string ftpRemotePath; #endregion public FtpHelper() { } /// <summary> /// 连接FTP服务器 /// </summary> /// <param name="FtpServerIP">FTP连接地址</param> /// <param name="FtpRemotePath">指定FTP连接成功后的当前目录, 如果不指定即默认为根目录</param> /// <param name="FtpUserID">用户名</param> /// <param name="FtpPassword">密码</param> public FtpHelper(string FtpRemotePath, string FtpUserID, string FtpPassword) { //ftpServerIP = FtpServerIP; ftpRemotePath = FtpRemotePath; ftpUserID = FtpUserID; ftpPassword = FtpPassword; //ftpURI = "ftp://" + ftpServerIP + "/" + ftpRemotePath + "/"; ftpURI = FtpRemotePath; } /// <summary> /// 上传 filename是本地图片的地址 /// </summary> public void Upload(string filePath, string filename) { FileInfo fileInf = new FileInfo(filename); FtpWebRequest reqFTP; reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(filePath + fileInf.Name)); reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); reqFTP.Method = WebRequestMethods.Ftp.UploadFile; reqFTP.KeepAlive = false; reqFTP.UseBinary = true; reqFTP.ContentLength = fileInf.Length; int buffLength = 2048; byte[] buff = new byte[buffLength]; int contentLen; FileStream fs = fileInf.OpenRead(); try { Stream strm = reqFTP.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(); } catch (Exception ex) { throw new Exception(ex.Message); } } /// <summary> /// 下载 filePath是下载到本机的地址,fileName是需要下载的文件的名字 /// </summary> public bool Download(string savePath, string ftpURI, string oldfileName, string newfileName) { try { string newPath = savePath + newfileName; string path = newPath.Substring(0, newPath.LastIndexOf("\\")); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } FileStream outputStream = new FileStream(newPath, FileMode.Create);//存储路径 FtpWebRequest reqFTP; reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + oldfileName));//ftp文件路径 reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); reqFTP.Method = WebRequestMethods.Ftp.DownloadFile; reqFTP.UseBinary = true; FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse(); Stream ftpStream = response.GetResponseStream(); //每次读取2048字节 //int bufferSize = 0; //int msgnum = (int)(ftpStream.Length / 2048); //int othermsg = (int)(ftpStream.Length % 2048); //if (msgnum == 0) //不到2048字节 //{ // bufferSize = (int)ftpStream.Length; // int readCount; // byte[] buffer = new byte[bufferSize]; // readCount = ftpStream.Read(buffer, 0, bufferSize); // if (readCount > 0) // { // outputStream.Write(buffer, 0, readCount); // //readCount = ftpStream.Read(buffer, 0, bufferSize); // } //} //else //{ // int offset = 0; // bufferSize = 2048; // byte[] buffer = new byte[bufferSize]; // for (int i = 0; i < msgnum; i++) // { // buffer = new byte[bufferSize]; // offset = i * 2048; // ftpStream.Seek(offset, SeekOrigin.Begin); // ftpStream.Read(buffer, 0, bufferSize); // outputStream.Write(buffer, 0, bufferSize); // } // if(othermsg > 0) // { // buffer = new byte[othermsg]; // offset = msgnum * 2048; // ftpStream.Seek(offset, SeekOrigin.Begin); // ftpStream.Read(buffer, 0, othermsg); // outputStream.Write(buffer, 0, othermsg); // } //} MemoryStream ms = StreamToMemoryStream(ftpStream); ms.Seek(0, SeekOrigin.Begin);//设置复制开始的地方 ms.CopyTo(outputStream); ftpStream.Close(); outputStream.Close(); response.Close(); ms.Close(); return true; } catch (Exception ex) { return false; //throw new Exception(ex.Message); } } public MemoryStream StreamToMemoryStream(Stream instream) { MemoryStream outstream = new MemoryStream(); const int bufferLen = 4096; byte[] buffer = new byte[1024 * 1024 * 5]; int count = 0; while ((count = instream.Read(buffer, 0, bufferLen)) > 0)//Read和Write会偏移所以offset不需要变 { outstream.Write(buffer, 0, count); } return outstream; } /// <summary> /// 删除服务器的文件 fileName是需要删除的文件的名字 /// </summary> public void Delete(string ftpURI, string fileName) { try { FtpWebRequest reqFTP; reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + fileName)); reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); reqFTP.Method = WebRequestMethods.Ftp.DeleteFile; reqFTP.KeepAlive = false; string result = String.Empty; FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse(); long size = response.ContentLength; Stream datastream = response.GetResponseStream(); StreamReader sr = new StreamReader(datastream); result = sr.ReadToEnd(); sr.Close(); datastream.Close(); response.Close(); } catch (Exception ex) { throw new Exception(ex.Message); } } List<string> Test = new List<string>(); /// <summary> /// 获取当前目录下明细(包含文件和文件夹) /// </summary> public List<DirInfos> GetFilesDetailList(string url, ref string errstr) { List<DirInfos> Dirs = new List<DirInfos>(); try { if (url == "") { url = ftpURI; } //StringBuilder result = new StringBuilder(); FtpWebRequest ftp; ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(url)); ftp.Credentials = new NetworkCredential(ftpUserID, ftpPassword); ftp.Method = WebRequestMethods.Ftp.ListDirectoryDetails; WebResponse response = ftp.GetResponse(); using (StreamReader reader = new StreamReader(response.GetResponseStream())) { string line = ""; while ((line = reader.ReadLine()) != null) { Test.Add(line); #if Test if (line.Contains("DIR")) { //result.Append(line); //result.Append("\n"); DirInfos dir = new DirInfos(); dir.name = line.Substring(line.LastIndexOf(">") + 1).Replace(" ", ""); dir.time = line.Substring(0, line.LastIndexOf("<")).Trim(); dir.files = new List<FileInfos>(); dir.files = GetFileList(url, dir.name); Dirs.Add(dir); } else { DirInfos dir = new DirInfos(); dir.name = ""; dir.time = ""; dir.files = new List<FileInfos>(); FileInfos file = new FileInfos(); file.time = line.Substring(0, line.IndexOf("M") + 1); string str = line.Substring(line.IndexOf("M") + 1).Trim(); string[] tmpstr = str.Split(' '); if (tmpstr.Length > 1) { file.name = tmpstr[1]; file.size = tmpstr[0]; } dir.files.Add(file); Dirs.Add(dir); } #else /////////linux系统返回不一样 //line = "-rwxr-xr-x 1 0 0 375 Jul 04 2023 usb_device_disk.sh"; string[] tmpstr = line.Split(' '); tmpstr = tmpstr.Where(s => !string.IsNullOrEmpty(s)).ToArray();//移除空字符 int lenth = tmpstr.Length; if (lenth > 0) { if (tmpstr[0].Contains("d")) { //result.Append(line); //result.Append("\n"); DirInfos dir = new DirInfos(); if (lenth > 4) { dir.name = tmpstr[lenth - 1]; dir.time = string.Format("{0}-{1}-{2}", tmpstr[lenth - 4], tmpstr[lenth - 3], tmpstr[lenth - 2]); dir.files = new List<FileInfos>(); dir.files = GetFileList(url, dir.name); Dirs.Add(dir); } } else { DirInfos dir = new DirInfos(); dir.name = ""; dir.time = ""; dir.files = new List<FileInfos>(); FileInfos file = new FileInfos(); if (lenth > 4) { file.name = tmpstr[lenth - 1]; file.time = string.Format("{0}-{1}-{2}", tmpstr[lenth - 4], tmpstr[lenth - 3], tmpstr[lenth - 2]); file.size = tmpstr[lenth - 5]; } dir.files.Add(file); Dirs.Add(dir); } } #endif } //if(result.ToString().Contains("\n")) //{ // result.Remove(result.ToString().LastIndexOf("\n"), 1); //} reader.Close(); response.Close(); } //return result.ToString().Split('\n'); } catch (Exception ex) { //throw new Exception(ex.Message); errstr = ex.ToString(); Dirs = null; } //FilesHelper.FileSaveXml(Test, System.AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "Test.xml"); return Dirs; } public List<DirInfos> GetFilesDetailList_Wifi(int type, DateTime startTime, DateTime endTime, ref string errstr) { int retryCnt = 0; bool enterFlg = false; List<DirInfos> Dirs = new List<DirInfos>(); MainForm.mainform.CurrentSerial.RevFinishFlg[(int)EnumRevType.GetFileNames] = false; try { byte[] sendData = ComonClass.GetFileNamesBytes(type, startTime, endTime); ComonClass.SendFunc(sendData); do { //等待数据接收完毕 if (MainForm.mainform.CurrentSerial.RevFinishFlg[(int)EnumRevType.GetFileNames]) { Dirs = AnalyzeFileNames(MainForm.mainform.CurrentSerial.RevBuff.ToArray(), ref errstr); enterFlg = true; break; } else { Thread.Sleep(10); retryCnt++; } } while (retryCnt < 300); if(!enterFlg) { errstr = "获取文件列表失败!无回复。"; Dirs = null; } } catch (Exception ex) { errstr = ex.ToString(); Dirs = null; } return Dirs; } private List<DirInfos> AnalyzeFileNames(byte[] revBuff, ref string errstr) { List<DirInfos> Dirs = new List<DirInfos>(); if(revBuff[3] == 1) //时间范围有效 { for (int j = 4; j < revBuff.Length - 2; ) { DirInfos dir = new DirInfos(); dir.name = string.Format("{0}-{1}-{2}", revBuff[0 + j] + 1900, revBuff[1 + j], revBuff[2 + j]); dir.time = dir.name; dir.files = new List<FileInfos>(); int fileNum = (revBuff[3 + j] << 24) + (revBuff[4 + j] << 16) + (revBuff[5 + j] << 8) + (revBuff[6 + j]); for (int i = 0; i < fileNum; i++) { FileInfos file = new FileInfos(); int skipNum = j + (i * 3); file.name = string.Format("{0} {1}-{2}-{3}.dat", dir.name, revBuff[7 + skipNum], revBuff[8 + skipNum], revBuff[9 + skipNum]); dir.files.Add(file); } j += 3 + 4 + fileNum * 3; Dirs.Add(dir); } } else { errstr = string.Format("配置的时间范围无效!有效时间:{0}-{1}-{2}~{3}-{4}-{5}", revBuff[4], revBuff[5], revBuff[6], revBuff[7], revBuff[8], revBuff[9]); } return Dirs; } /// <summary> /// 获取FTP文件列表(包括文件夹) /// </summary> public string[] GetAllList(string url) { List<string> list = new List<string>(); FtpWebRequest req = (FtpWebRequest)WebRequest.Create(new Uri(url)); req.Credentials = new NetworkCredential(ftpUserID, ftpPassword); req.Method = WebRequestMethods.Ftp.ListDirectory; req.UseBinary = true; req.UsePassive = true; try { using (FtpWebResponse res = (FtpWebResponse)req.GetResponse()) { using (StreamReader sr = new StreamReader(res.GetResponseStream())) { string s; while ((s = sr.ReadLine()) != null) { list.Add(s); } } } } catch (Exception ex) { throw (ex); } return list.ToArray(); } /// <summary> /// 获取当前目录下文件列表(不包括文件夹) /// </summary> public List<FileInfos> GetFileList(string ftpURI, string url) { List<FileInfos> files = new List<FileInfos>(); //StringBuilder result = new StringBuilder(); FtpWebRequest reqFTP; try { if (url != "") { url = ftpURI + url + "/"; } else { url = ftpURI; } reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(url)); reqFTP.UseBinary = true; reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); reqFTP.Method = WebRequestMethods.Ftp.ListDirectoryDetails; WebResponse response = reqFTP.GetResponse(); StreamReader reader = new StreamReader(response.GetResponseStream()); string line = ""; while ((line = reader.ReadLine()) != null) { #if !Test string[] tmpstr = line.Split(' '); tmpstr = tmpstr.Where(s => !string.IsNullOrEmpty(s)).ToArray();//移除空字符 int lenth = tmpstr.Length; if (lenth > 0) { if (!tmpstr[0].Contains("d")) #else { if (!line.Contains("DIR")) #endif { //result.Append(Regex.Match(line, @"[\S]+ [\S]+", RegexOptions.IgnoreCase).Value.Split(' ')[1]); //result.Append("\n"); #if Test /////////windows FileInfos file = new FileInfos(); file.time = line.Substring(0, line.IndexOf("M") + 1); string str = line.Substring(line.IndexOf("M") + 1).Trim(); string[] tmpstr = str.Split(' '); if (tmpstr.Length > 1) { file.name = tmpstr[1]; file.size = tmpstr[0]; files.Add(file); } #else /////////linux FileInfos file = new FileInfos(); if (lenth > 4) { file.name = tmpstr[lenth - 1]; file.time = string.Format("{0}-{1}-{2}", tmpstr[lenth - 4], tmpstr[lenth - 3], tmpstr[lenth - 2]); file.size = tmpstr[lenth - 5]; files.Add(file); } #endif } } //line = reader.ReadLine(); } //result.Remove(result.ToString().LastIndexOf('\n'), 1); reader.Close(); response.Close(); } catch (Exception ex) { //throw (ex); } return files; } /// <summary> /// 获取指定文件夹下文件列表(不包括文件夹) /// </summary> public string[] GetFileList2(string mask, string dirName) { string[] downloadFiles = null; StringBuilder result = new StringBuilder(); FtpWebRequest reqFTP; try { reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + dirName)); reqFTP.UseBinary = true; reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); reqFTP.Method = WebRequestMethods.Ftp.ListDirectory; WebResponse response = reqFTP.GetResponse(); StreamReader reader = new StreamReader(response.GetResponseStream(), Encoding.Default); string line = reader.ReadLine(); while (line != null) { if (mask.Trim() != string.Empty && mask.Trim() != "*.*") { string mask_ = mask.Substring(0, mask.IndexOf("*")); if (line.Substring(0, mask_.Length) == mask_) { result.Append(line); result.Append("\n"); } } else { result.Append(line); result.Append("\n"); } line = reader.ReadLine(); } if (result.Length == 0) return new string[] { }; result.Remove(result.ToString().LastIndexOf('\n'), 1); reader.Close(); response.Close(); downloadFiles = result.ToString().Split('\n'); } catch (Exception ex) { return new string[] { }; } if (downloadFiles == null) downloadFiles = new string[] { }; return downloadFiles; } /// <summary> /// 判断当前目录下指定的文件是否存在 /// </summary> /// <param name="RemoteFileName">远程文件名</param> //public bool FileExist(string RemoteFileName) //{ // string[] fileList = GetFileList("*.*"); // foreach (string str in fileList) // { // if (str.Trim() == RemoteFileName.Trim()) // { // return true; // } // } // return false; //} /// <summary> /// 创建文件夹 /// </summary> public void MakeDir(string ftpURI, string dirName) { FtpWebRequest reqFTP; try { reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + dirName)); reqFTP.Method = WebRequestMethods.Ftp.MakeDirectory; reqFTP.UseBinary = true; reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse(); Stream ftpStream = response.GetResponseStream(); ftpStream.Close(); response.Close(); } catch (Exception ex) { } } /// <summary> /// 获取指定文件大小 /// </summary> public long GetFileSize(string ftpURI, string filename) { FtpWebRequest reqFTP; long fileSize = 0; try { reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + filename)); reqFTP.Method = WebRequestMethods.Ftp.GetFileSize; reqFTP.UseBinary = true; reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse(); Stream ftpStream = response.GetResponseStream(); fileSize = response.ContentLength; ftpStream.Close(); response.Close(); } catch (Exception ex) { } return fileSize; } /// <summary> /// 更改文件名 /// </summary> public void ReName(string ftpURI, string currentFilename, string newFilename) { FtpWebRequest reqFTP; try { reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + currentFilename)); reqFTP.Method = WebRequestMethods.Ftp.Rename; reqFTP.RenameTo = newFilename; reqFTP.UseBinary = true; reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse(); Stream ftpStream = response.GetResponseStream(); ftpStream.Close(); response.Close(); } catch (Exception ex) { } } /// <summary> /// 移动文件 /// </summary> public void MovieFile(string ftpURI, string currentFilename, string newDirectory) { ReName(ftpURI, currentFilename, newDirectory); } /// <summary> /// 切换当前目录 /// </summary> /// <param name="IsRoot">true:绝对路径 false:相对路径</param> public void GotoDirectory(string ftpURI, string DirectoryName, bool IsRoot) { if (IsRoot) { ftpRemotePath = DirectoryName; } else { ftpRemotePath += DirectoryName + "/"; } ftpURI = "ftp://" + ftpServerIP + "/" + ftpRemotePath + "/"; } /// <summary> /// 删除ftpURI目录下指定的文件夹 /// </summary> public void deleteDir(string ftpURI, string dirName) { FtpWebRequest reqFTP; try { reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpURI + dirName)); reqFTP.Method = WebRequestMethods.Ftp.RemoveDirectory; reqFTP.UseBinary = true; reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword); FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse(); Stream ftpStream = response.GetResponseStream(); ftpStream.Close(); response.Close(); } catch (Exception ex) { throw new Exception(ex.Message); } } } public class DirInfos { /// <summary> /// /// </summary> public string name { get; set; } /// <summary> /// /// </summary> public string time { get; set; } /// <summary> /// /// </summary> public string size { get; set; } /// <summary> /// /// </summary> public List<FileInfos> files { get; set; } } public class FileInfos { public string name { get; set; } public string time { get; set; } public string size { get; set; } }
参考:https://blog.csdn.net/weixin_44634727/article/details/113338024
三、问题汇总
1、连接超时等待时间太长
本身网络出错,设置Timeout属性不生效,配置KeepAlive、UseBinary、Proxy等方法也没有用。主要是GetResponse()函数执行时间很长。
以下办法通过检测GetResponse()函数执行时间判断是否超时:
FtpWebRequest ftp;
WebResponse response;
/// <summary> /// 执行函数超时,Action表示委托,函数没有参数没有返回值 /// </summary> /// <param name="action"></param> /// <param name="timeoutMilliseconds"></param> private bool CallWithTimeout(Action action, int timeoutMilliseconds) { bool res = true; Thread threadToKill = null; Action wrappedAction = () => { threadToKill = Thread.CurrentThread; action(); }; IAsyncResult result = wrappedAction.BeginInvoke(null, null); try { if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds)) { wrappedAction.EndInvoke(result); } else { threadToKill.Abort(); res = false; } } catch { res = false; } return res; }
private void GetResponse()
{
response = ftp.GetResponse();
}
调用:
ftp = (FtpWebRequest)FtpWebRequest.Create(new Uri(ftpulr));//ftp文件路径 ftp.Credentials = new NetworkCredential(ftpUserID, ftpPassword); ftp.Method = WebRequestMethods.Ftp.DownloadFile; ftp.UseBinary = true; if (!CallWithTimeout(GetResponse, Timeout)) { ftp.Abort(); ftp = null; errstr = "FTP服务器连接超时!"; return false; } //response = (FtpWebResponse)ftp.GetResponse(); Stream ftpStream = response.GetResponseStream();
//....
参考:https://blog.csdn.net/FL1623863129/article/details/103333226
/*******相与枕藉乎舟中,不知东方之既白*******/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)