|
Dieses Beispiel zeigt euch wie man ausschließlich mit Hilfe der .NET FTP Klassen einen rekursiven FTP Download realisieren kann.
Im Internet liest man zwar oft dass die .NET FTP Klassen
nicht das Gelbe vom Ei sind ;-)
LizenzDiese Software unterliegt der GNU Lesser General Public License (LGPL).
Wie ist es dazu gekommen?Grund dafür war dass ich für mich persönlich ein kleines FTP
Backup Tool benötigte.
Downloads und LinksDas vollständige Codesample (Visual Studio 2005 Projekt)
könnt ihr hier herunterladen:
Das Demoprogramm (ausführbare EXE, Konsolenanwendung) gibt es
hier:
Dieses Code Beispiel wird demnächst auch bei CodeProject veröffentlicht.
ToDo· Ladet man Dateien von einem Linux-Server herunter so kann es sein dass dort Dateien/Ordner vorhanden sind welche sich nur in der Groß-/ Kleinschreibung unterscheiden. Dies ist denke ich allerdings ein allgemeines Problem bei FTP Downloads unter Windows von einem Linux Server. Sollte hier jemand einen Lösungsvorschlag haben würde ich mich sehr darüber freuen!
Sourcecode
private static void ListFtpDir(string strHost, string strUser, string strPass, string strDir, int iMaxTries, int iTimeout, out List<string> strDirs, out List<string> strFiles) { if (strDir.EndsWith("/")) { strDir = strDir.Substring(0, strDir.Length - 1); }
strDirs = new List<string>(); strFiles = new List<string>();
//Try to get the directory listing for <iTry> times for (int iTry = 1; iTry <= iMaxTries; iTry++) { try { Console.WriteLine(string.Format("Reading directory \"{0}\" (Try #{1})", strDir, iTry));
//Reset the content of the lists if previous tries have failed strDirs = new List<string>(); strFiles = new List<string>();
//Create the FTP request FtpWebRequest ftpReq = WebRequest.Create("ftp://" + strHost + strDir) as FtpWebRequest; //Setting KeepAlive true makes .NET using the same connection ftpReq.KeepAlive = true; //Get the directory listing instead of files ftpReq.Method = WebRequestMethods.Ftp.ListDirectoryDetails; //Set authentication ftpReq.Credentials = new NetworkCredential(strUser, strPass); //Set timeout (milliseconds). ftpReq.Timeout = iTimeout;
FtpWebResponse ftpResp = ftpReq.GetResponse() as FtpWebResponse;
Stream s = ftpResp.GetResponseStream(); //I set encoding to "iso-8859-1" because i will get german umlauts in file and directory names //Feel free to change this to an appropriate encoding //Maybe someone know a way to get the correct encoding from the server? StreamReader sr = new StreamReader(s, Encoding.GetEncoding("iso-8859-1"));
while (!sr.EndOfStream) { string strLine = sr.ReadLine();
//Skip empty lines (should not happen) if (strLine.Trim().Length == 0) { continue; }
//If the line starts with character 'd' then it is a directory //(first part of the directory listing are the file/directory attributes) bool bIsDir = (strLine[0] == 'd');
//Because we just need the file/directory name we remove all other informations //We start to parse the line from the beginning because a file/directory name //can contain trailing spaces //Feel free to parse some of these informations (the comments may help you)
//Remove attributes strLine = strLine.Substring(10).TrimStart(null);
//Remove number of sub elements strLine = strLine.Substring(strLine.IndexOf(' ')).TrimStart(null);
//Remove owner strLine = strLine.Substring(strLine.IndexOf(' ')).TrimStart(null);
//Remove group strLine = strLine.Substring(strLine.IndexOf(' ')).TrimStart(null);
//Remove size strLine = strLine.Substring(strLine.IndexOf(' ')).TrimStart(null);
//Remove month strLine = strLine.Substring(strLine.IndexOf(' ')).TrimStart(null);
//Remove day strLine = strLine.Substring(strLine.IndexOf(' ')).TrimStart(null);
//Remove year or time strLine = strLine.Substring(strLine.IndexOf(' ')).TrimStart(null);
//The remaining line is the file/directory name string strFileName = strLine;
//If the filename is a dot or double dot then skip this if (strFileName == "." || strFileName == "..") { continue; }
//Add file/directory to the list if (bIsDir) { strDirs.Add(strFileName); } else { strFiles.Add(strFileName); } }
sr.Close(); sr.Dispose(); sr = null;
s.Close(); s.Dispose(); s = null;
//If KeepAlive is set to true the connection remains open anyway ftpResp.Close();
strDirs.Sort(); strFiles.Sort();
break; } catch (Exception ex) { //If we have reached the maximum try count rethrow the exception if (iTry == iMaxTries) { throw ex; }
Thread.Sleep(5000); } } }
private static void DownloadFtpDirRecursive(string strHost, string strUser, string strPass, string strDir, string strLocalDir, int iMaxTries, int iTimeout) { List<string> strDirs; List<string> strFiles;
if (strDir.EndsWith("/")) { strDir = strDir.Substring(0, strDir.Length - 1); }
//If the target directory doesn't exist create it if (!Directory.Exists(strLocalDir)) { Directory.CreateDirectory(strLocalDir); }
//Get files and directories in current directory ListFtpDir(strHost, strUser, strPass, strDir, iMaxTries, iTimeout, out strDirs, out strFiles);
//Buffer for retrieving data byte[] byBuffer = new byte[4096]; int iRead;
foreach (string strFile in strFiles) { //Try to get the file for <iTry> times for (int iTry = 1; iTry <= iMaxTries; iTry++) { try { Console.WriteLine(string.Format("Downloading file \"{0}\" (Try #{1})", strDir + "/" + strFile, iTry));
//Create the FTP request FtpWebRequest ftpReq = WebRequest.Create("ftp://" + strHost + strDir + "/" + strFile) as FtpWebRequest; //Setting KeepAlive true makes .NET using the same connection ftpReq.KeepAlive = true; //Get the file ftpReq.Method = WebRequestMethods.Ftp.DownloadFile; //Set authentication ftpReq.Credentials = new NetworkCredential(strUser, strPass); //Use binary transfer - it's the safest way ftpReq.UseBinary = true; //Set timeout (milliseconds). ftpReq.Timeout = iTimeout;
FtpWebResponse ftpResp = ftpReq.GetResponse() as FtpWebResponse;
//Create the target file FileStream fs = new FileStream(Path.Combine(strLocalDir, strFile), FileMode.Create, FileAccess.Write, FileShare.None);
Stream s = ftpResp.GetResponseStream();
//Read from the response stream and write it into the target file while (true) { iRead = s.Read(byBuffer, 0, byBuffer.Length);
if (iRead < 1) { break; }
fs.Write(byBuffer, 0, iRead); }
s.Close(); s.Dispose(); s = null;
fs.Close(); fs.Dispose(); fs = null;
//If KeepAlive is set to true the connection remains open anyway ftpResp.Close();
break; } catch (Exception ex) { //If we have reached the maximum try count rethrow the exception if (iTry == iMaxTries) { throw ex; }
Thread.Sleep(5000); } } }
//Follow the subdirectories recursively foreach (string strSubDir in strDirs) { DownloadFtpDirRecursive(strHost, strUser, strPass, strDir + "/" + strSubDir, Path.Combine(strLocalDir, strSubDir), iMaxTries, iTimeout); } } | |