using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; namespace AR.FTPClient { public partial class FTPClient : IFTPClient { public string errorMessage { get; set; } private int bufferSize = 2048; private int timeout = 20000; #region "Contstruct" public FTPClient() : this("127.0.0.1", "Anonymous", "") { } public FTPClient(string Host, string UserID, string UserPass) : this(Host, UserID, UserPass, 21) { } public FTPClient(string Host, string UserID, string UserPass, bool passive) : this(Host, UserID, UserPass, 21, passive) { } public FTPClient(string Host, string UserID, string UserPass, int port) : this(Host, UserID, UserPass, port, false) { } public FTPClient(string host, string userid, string userpass, int port, bool passive) { Host = host; UserID = userid; UserPassword = userpass; Port = port; PassiveMode = passive; this.TextEncoding = System.Text.Encoding.UTF8; } #endregion public event EventHandler Message; public event EventHandler ListPrgress; #region "Properties" /// /// 접속하려는 FTP서버의 IP혹은 도메인 주소를 입력하세요 /// 기본값 : 127.0.0.1 /// public string Host { get; set; } /// /// FTP의 사용자 ID /// 기본값 : Anonymous /// public string UserID { get; set; } /// /// FTP 사용자 ID의 암호 /// 기본값 : 없음 /// public string UserPassword { get; set; } /// /// FTP 접속 포트 /// 기본값 : 21 /// public int Port { get; set; } /// /// FTP 접속 방식 (능동형/수동형) /// public bool PassiveMode { get; set; } /// /// 문자수신시 사용할 인코딩 방식 /// public System.Text.Encoding TextEncoding { get; set; } #endregion public void Dispose() { } /// /// 파일을 다운로드 합니다. /// /// 원격위치의 전체 경로 /// 로컬위치의 전체 경로 public Boolean Download(string remoteFile, string localFile, bool overwrite = false) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; //overwrite funcion - 190622 - chi errorMessage = string.Empty; if (overwrite == false && System.IO.File.Exists(localFile)) { errorMessage = "Target file alreay exists"; return false; } else if (overwrite == true && System.IO.File.Exists(localFile)) { try { System.IO.File.Delete(localFile); } catch (Exception ex) { errorMessage = ex.Message; return false; } } bool retval = true; try { long Receive = 0; var url = CombineUrl(remoteFile); ftpRequest = (FtpWebRequest)FtpWebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(this.UserID, this.UserPassword); ftpRequest.UseBinary = true; ftpRequest.UsePassive = this.PassiveMode; ftpRequest.KeepAlive = true; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpStream = ftpResponse.GetResponseStream(); FileStream localFileStream = new FileStream(localFile, FileMode.Create); byte[] byteBuffer = new byte[bufferSize]; int bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize); Receive += bytesRead; if (ListPrgress != null) ListPrgress(this, new ListProgressEventArgs(Receive)); try { while (bytesRead > 0) { localFileStream.Write(byteBuffer, 0, bytesRead); bytesRead = ftpStream.Read(byteBuffer, 0, bufferSize); Receive += bytesRead; if (ListPrgress != null) ListPrgress(this, new ListProgressEventArgs(Receive)); } } catch (Exception ex) { retval = false; Console.WriteLine(ex.ToString()); } localFileStream.Close(); ftpStream.Close(); ftpResponse.Close(); ftpRequest = null; } catch (Exception ex) { retval = false; this.errorMessage = ex.Message; Console.WriteLine(ex.ToString()); } return retval; } /// /// 파일을 업로드 합니다. /// /// 원격위치의 전체 경로 /// 로컬위치의 전체 경로 /// public Boolean Upload(string remoteFile, string localFile) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; try { var url = CombineUrl(remoteFile); ftpRequest = (FtpWebRequest)FtpWebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UseBinary = true; ftpRequest.UsePassive = PassiveMode; ftpRequest.KeepAlive = true; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.Method = WebRequestMethods.Ftp.UploadFile; ftpStream = ftpRequest.GetRequestStream(); FileStream localFileStream = new FileStream(localFile, FileMode.Open); byte[] byteBuffer = new byte[bufferSize]; int bytesSent = localFileStream.Read(byteBuffer, 0, bufferSize); Boolean bError = false; try { System.Diagnostics.Stopwatch wat = new System.Diagnostics.Stopwatch(); wat.Restart(); while (bytesSent != 0) { ftpStream.Write(byteBuffer, 0, bytesSent); bytesSent = localFileStream.Read(byteBuffer, 0, bufferSize); } } catch (Exception ex) { bError = true; } localFileStream.Close(); ftpStream.Close(); ftpRequest = null; if (bError) return false; else return true; } catch (Exception ex) { this.errorMessage = ex.Message; return false; } } /// /// 원격파일을 삭제 합니다. /// /// public Boolean Delete(string remotefile) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; try { var url = CombineUrl(remotefile); ftpRequest = (FtpWebRequest)WebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UseBinary = true; ftpRequest.UsePassive = this.PassiveMode; ftpRequest.KeepAlive = true; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.Method = WebRequestMethods.Ftp.DeleteFile; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpResponse.Close(); ftpRequest = null; return true; } catch (Exception ex) { this.errorMessage = ex.Message; return false; } } /// /// 원격파일의 이름을 변경 합니다. /// /// /// public Boolean rename(string currentFileNameAndPath, string newFileName) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; try { var url = CombineUrl(currentFileNameAndPath); ftpRequest = (FtpWebRequest)WebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UsePassive = this.PassiveMode; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.UseBinary = true; ftpRequest.KeepAlive = true; ftpRequest.Method = WebRequestMethods.Ftp.Rename; ftpRequest.RenameTo = newFileName; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpResponse.Close(); ftpRequest = null; return true; } catch (Exception ex) { this.errorMessage = ex.Message; return false; } } /// /// 원격위치에 폴더를 생성 합니다. /// 트리구조로 폴더를 생성하지 않습니다. 여러개의 폴더를 생성하려면 각각 호출 해야 합니다. /// /// /// public bool createDirectory(string newDirectory) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; try { var url = CombineUrl(newDirectory, false); ftpRequest = (FtpWebRequest)WebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UsePassive = this.PassiveMode; ftpRequest.UseBinary = true; ftpRequest.KeepAlive = true; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.Method = WebRequestMethods.Ftp.MakeDirectory; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpResponse.Close(); ftpRequest = null; return true; } catch (Exception ex) { this.errorMessage = ex.Message; return false; } } /// /// 원격위치의 폴더를 삭제합니다. 폴더 삭제 전 대상 폴더는 비어있어야 합니다. /// /// /// public bool RemoveDirectory(string Directory) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; try { var url = CombineUrl(Directory, false); ftpRequest = (FtpWebRequest)WebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UsePassive = this.PassiveMode; ftpRequest.UseBinary = true; ftpRequest.KeepAlive = true; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.Method = WebRequestMethods.Ftp.RemoveDirectory; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpResponse.Close(); ftpRequest = null; return true; } catch (Exception ex) { this.errorMessage = ex.Message; return false; } } /// /// 파일의 생성일자 반환 /// /// /// public string getFileCreatedDateTime(string fileName) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; try { var url = CombineUrl(fileName); ftpRequest = (FtpWebRequest)WebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UsePassive = this.PassiveMode; ftpRequest.UseBinary = true; ftpRequest.KeepAlive = true; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.Method = WebRequestMethods.Ftp.GetDateTimestamp; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpStream = ftpResponse.GetResponseStream(); StreamReader ftpReader = new StreamReader(ftpStream, this.TextEncoding); string fileInfo = null; try { fileInfo = ftpReader.ReadToEnd(); } catch (Exception ex) { Console.WriteLine(ex.ToString()); } ftpReader.Close(); ftpStream.Close(); ftpResponse.Close(); ftpRequest = null; return fileInfo; } catch (Exception ex) { this.errorMessage = ex.Message; Console.WriteLine(ex.ToString()); } return ""; } /// /// 파일의 크기를 반환 /// /// /// public string getFileSize(string fileName) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; try { var url = CombineUrl(fileName); ftpRequest = (FtpWebRequest)WebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UseBinary = true; ftpRequest.UsePassive = this.PassiveMode; ftpRequest.KeepAlive = true; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.Method = WebRequestMethods.Ftp.GetFileSize; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpStream = ftpResponse.GetResponseStream(); StreamReader ftpReader = new StreamReader(ftpStream, this.TextEncoding); string fileInfo = null; try { while (ftpReader.Peek() != -1) { fileInfo = ftpReader.ReadToEnd(); } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } ftpReader.Close(); ftpStream.Close(); ftpResponse.Close(); ftpRequest = null; return fileInfo; } catch (Exception ex) { errorMessage = ex.Message; Console.WriteLine(ex.ToString()); } return ""; } /// /// 폴더와 파일의 이름만 반환 합니다. /// /// /// public string[] directoryListSimple(string directory) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; errorMessage = string.Empty; try { var url = CombineUrl(directory, false); if (url.EndsWith("/") == false) url += "/"; ftpRequest = (FtpWebRequest)WebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UseBinary = true; ftpRequest.UsePassive = PassiveMode; ftpRequest.KeepAlive = true; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.ReadWriteTimeout = 30000; ftpRequest.Timeout = 30000; ftpRequest.Method = WebRequestMethods.Ftp.ListDirectory; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpStream = ftpResponse.GetResponseStream(); StreamReader ftpReader = new StreamReader(ftpStream, this.TextEncoding); string directoryRaw = null; try { while (ftpReader.Peek() != -1) { var dataLIne = ftpReader.ReadLine(); if (dataLIne.Trim() != "") { if (directoryRaw != null && directoryRaw != "") directoryRaw += "|"; directoryRaw += dataLIne; } } } catch (Exception ex) { errorMessage += "\n" + ex.Message; } ftpReader.Close(); ftpStream.Close(); ftpResponse.Close(); ftpRequest = null; try { string[] directoryList = directoryRaw.Split("|".ToCharArray()); return directoryList; } catch (Exception ex) { errorMessage += "\n" + ex.Message; } } catch (Exception ex) { errorMessage = ex.Message; Console.WriteLine(ex.ToString()); } return new string[] { "" }; } /// /// 폴더 및 파일의 정보를 상세하게 표시합니다. /// /// /// public FTPdirectory ListDirectoryDetail(string directory) { FtpWebRequest ftpRequest = null; FtpWebResponse ftpResponse = null; Stream ftpStream = null; errorMessage = string.Empty; try { var url = CombineUrl(directory, false); if (url.EndsWith("/") == false) url += "/"; ftpRequest = (FtpWebRequest)WebRequest.Create(url); ftpRequest.Credentials = new NetworkCredential(UserID, UserPassword); ftpRequest.UsePassive = true; ftpRequest.UseBinary = true; ftpRequest.KeepAlive = false; ftpRequest.ReadWriteTimeout = this.timeout; ftpRequest.ReadWriteTimeout = 30000; ftpRequest.Timeout = 30000; ftpRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails; ftpResponse = (FtpWebResponse)ftpRequest.GetResponse(); ftpStream = ftpResponse.GetResponseStream(); StreamReader ftpReader = new StreamReader(ftpStream, this.TextEncoding); string directoryRaw = null; try { while (ftpReader.Peek() != -1) { directoryRaw += ftpReader.ReadLine() + "\r"; } } catch (Exception ex) { errorMessage += "\n" + ex.Message; } ftpReader.Close(); ftpStream.Close(); ftpResponse.Close(); ftpRequest = null; directoryRaw = directoryRaw.Replace("\r\n", "\r").TrimEnd((char)0x0D); return new FTPdirectory(directoryRaw, directory); } catch (Exception ex) { errorMessage += "\n" + ex.Message; } return null; } #region "utillity" /// /// Url을 Host와 결합하여 생성 /// /// 경로명 /// 파일인지 여부 /// string CombineUrl(string file, bool isfile = true) { file = file.Replace("\\", "/"); string url = this.Host; if (this.Host.ToLower().StartsWith("ftp://") == false) url = "ftp://" + url; if (url.Substring(5).LastIndexOf(':') == -1) url += ":" + this.Port.ToString(); if (this.Host.EndsWith("/")) url = this.Host.Substring(0, Host.Length - 1); if (file.StartsWith("/")) url = url + System.Web.HttpUtility.UrlPathEncode(file); else url = url + "/" + System.Web.HttpUtility.UrlPathEncode(file); url = url.Replace("#", "%23"); if (isfile) return url; else return url + "/"; } public string PathCombine(string path, string add) { var newpath = string.Empty; if (path.EndsWith("/")) newpath = path + add; else newpath = path + "/" + add; if (!newpath.EndsWith("/")) newpath += '/'; return newpath; } public string PathFileCombine(string path, string add) { var newpath = string.Empty; if (path.EndsWith("/")) newpath = path + add; else newpath = path + "/" + add; if (newpath.EndsWith("/")) newpath = newpath.Substring(0, newpath.Length - 1); return newpath; } public string getParent(string path) { if (path == "/") return path; else if (path == "") return "/"; else { //서브폴더를 찾아서 처리해준다. if (path.IndexOf('/') == -1) return "/"; else { if (path.EndsWith("/")) path = path.Substring(0, path.Length - 1); var slashindex = path.LastIndexOf('/'); var parent = path.Substring(0, slashindex); if (!parent.EndsWith("/")) parent += '/'; return parent; } } } public void RaiseMessage(Boolean isError, string message) { if (isError) errorMessage = message; //170920 if (Message != null) Message(this, new MessageEventArgs(isError, message)); } #endregion } }