[C# / winform] FTP Client 제작하기

 

FTP Client를 제작할 예정이다. 대표적인 FTP Client라고 한다면 File Zilla가 있다. 

필자는 이 프로그램을 모티브로 해서 제작 할 것이다.  

 

여기서 내가 설명할 기능은 다음과 같다.

- 서버와 연결

- 파일 업로드 / 다운로드 / 삭제

- 모든 디렉토리 보여주기(TreeView, ListView)

- Drag & Drop 기능

 

🟨 제작하기

이미 C#에는 FTP와 관련된 메서드가 잘 구현이 되어 있다. 이 메서드를 어떻게 사용하고 활용했는지 확인해보자.

FtpWebRequest / FtpWebResponse를 사용해서 FTP와 연결 후 파일을 업로드/다운로드 할 수 있다. 

더 자세한 내용은 아래 링크로 접속해서 확인하자.

 

FTP 다운로드/업로드 - C# 프로그래밍 배우기 (Learn C# Programming)

C#에서 FTP 사용하기 C#에서 FTP 다운로드, 업로드 등의 클라이언트 기능을 사용하기 위해서는 흔히 (1) FtpWebRequest / FtpWebResponse 혹은 (2) WebClient 를 사용한다. WebClient는 사용 방법이 더 간단하지만,

www.csharpstudy.com

 

🟥 FTP 서버 연결하는 메서드

// 서버 연결
public bool ConnectToServer(string ip, string userId, string pwd)
{
    this.isConnected = false;
    this.ipAddr = ip;
    this.userId = userId;
    this.pwd = pwd;

    string url = $"ftp://{this.ipAddr}/"; // 포트는 기본값 21을 사용하며, URL 끝에 '/'를 붙입니다.

    try
    {
        FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(url);
        ftpWebRequest.Credentials = new NetworkCredential(this.userId, this.pwd);
        ftpWebRequest.KeepAlive = false;
        ftpWebRequest.Method = WebRequestMethods.Ftp.ListDirectory; // 디렉토리 목록을 요청하여 연결을 테스트
        ftpWebRequest.UsePassive = true; // 일반적으로 사용되는 설정

        // FTP 서버 응답을 받습니다.
        using (FtpWebResponse response = (FtpWebResponse)ftpWebRequest.GetResponse())
        {
            // 응답이 성공적으로 반환되면 연결 성공으로 간주합니다.
            this.isConnected = true;
            return true;
        }
    }
    catch (Exception ex)
    {
        // 예외를 캐치하여 LastException에 저장하고, 이벤트를 발생시킵니다.
        this.LastException = ex;
        return false;
    }
}

 

IP, ID, PW를 매개변수로 입력을 받고 접속에 성공했으면 True를 실패했으면 False를 반환한다.

 

 

🟨 FTP 서버의 디렉토리 목록을 가져오는 메서드

// 디렉토리 목록을 가져오는 메서드
public List<string> GetDirectoryListing(string path)
{
	// 디렉토리 목록을 저장할 List 생성
    List<string> directories = new List<string>();
    try
    {
        string url = $@"FTP://{this.ipAddr}/{path}";
        FtpWebRequest ftpWebRequest = (FtpWebRequest)WebRequest.Create(url);
        ftpWebRequest.Credentials = new NetworkCredential(this.userId, this.pwd);
        ftpWebRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails;

        using (FtpWebResponse response = (FtpWebResponse)ftpWebRequest.GetResponse())
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
        {
            while (!reader.EndOfStream)
            {
                directories.Add(reader.ReadLine());
            }
        }
    }
    catch (Exception ex)
    {
        this.LastException = ex;

        System.Reflection.MemberInfo info = System.Reflection.MethodInfo.GetCurrentMethod();
        string id = $"{info.ReflectedType.Name}.{info.Name}";

        if (this.ExceptionEvent != null)
        {
            this.ExceptionEvent(id, ex);
        }
    }
    return directories;
}

 

FTP 디렉토리 목록을 가져올 List 변수를 만들고 FTP 연결이 되면 해당 변수에 디렉토리 목록을 추가한다.

 

🟫 파일 업로드 메서드

// 디렉토리 업로드 메서드
public bool UploadFile(string localFilePath, string remoteFilePath)
{
    try
    {
        FileInfo fileInfo = new FileInfo(localFilePath);
        string url = $"FTP://{this.ipAddr}/{remoteFilePath}";

        FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
        request.Method = WebRequestMethods.Ftp.UploadFile;
        request.Credentials = new NetworkCredential(this.userId, this.pwd);

        using (FileStream fileStream = fileInfo.OpenRead())
        using (Stream requestStream = request.GetRequestStream())
        {
            fileStream.CopyTo(requestStream);
        }

        using (FtpWebResponse response = (FtpWebResponse)request.GetResponse()){}
        return true;
    }
    catch (Exception ex)
    {
        this.LastException = ex;
        return false;
    }
}

 

 

🟫 파일 다운로드 메서드

//디렉토리 다운로드 메서드
public bool DownloadFile(string remoteFilePath, string localFilePath)
{
    try
    {
        string url = $"FTP://{this.ipAddr}/{remoteFilePath}".Replace("\\", "/");

        FtpWebRequest request = (FtpWebRequest)WebRequest.Create(url);
        request.Method = WebRequestMethods.Ftp.DownloadFile;
        request.Credentials = new NetworkCredential(this.userId, this.pwd);

        using (FtpWebResponse response = (FtpWebResponse) request.GetResponse())
        using (Stream responseStream = response.GetResponseStream())
        using (FileStream fileStream = new FileStream(localFilePath, FileMode.Create))
        {
            responseStream.CopyTo(fileStream);
        }
        return true;
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Failed to download file {remoteFilePath}: {ex.Message}");
        return false;
    }
}

 

새로운 FileStream 객체를 만들고 다운로드 받을 경로에 디렉토리를 저장하는 방식이다. 

 

🟪 FTP 모든 디렉토리를 Treeview 형태로 보여주는 메서드

 // remote Treeview 구성 메소드
 private void LoadDirectoryTree(string path)
 {
     ftpDirectory.Nodes.Clear();
     TreeNode rootNode = new TreeNode(path);
     ftpDirectory.Nodes.Add(rootNode);

     AddDirectoryNodes(rootNode, path);
     rootNode.Expand();
 }

 // remote 폴더, 파일 가져오는 메소드
 private void AddDirectoryNodes(TreeNode treeNode, string path)
 {
     List<string> directories = ftp.GetDirectoryListing(path);

     foreach (string dir in directories)
     {
         string[] parts = dir.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
         string name = parts[3];

         if (parts[2] == "<DIR>")
         {
             // 디렉토리인 경우
             TreeNode dirNode = new TreeNode(name);
             treeNode.Nodes.Add(dirNode);
             AddDirectoryNodes(dirNode, path + "/" + name);
         }
         else
         {
             // 파일인 경우
             TreeNode fileNode = new TreeNode(name);
             treeNode.Nodes.Add(fileNode);
         }
     }
 }

 

재귀 함수를 활용해서 폴더 접근시 하위 파일들을 모두 접근 한 후 다시 상위 폴더로 이동하는 로직을 가지고 있다.

 

 

🟩 FTP 모든 디렉토리를 ListView 형태로 보여주는 메서드

private void SettingRemoteListView(string sFullPath)
{
    try
    {
        listView2.Items.Clear();

        RemoteTextBox.Text = sFullPath;

        List<string> directories = ftp.GetDirectoryListing(sFullPath);
        List<string> files = ftp.GetDirectoryListing(sFullPath);

        int DirectCount = 0;
        foreach (string dir in directories)
        {
            string[] parts = dir.Split(new[] { ' ' }, 9, StringSplitOptions.RemoveEmptyEntries);
            string name = parts[3];
            string date = parts[0] + " " + parts[1];
            // 원본 데이터 형식 정의
            string originalFormat = "MM-dd-yy hh:mmtt";

            // DateTime 객체로 변환
            DateTime dateTime = DateTime.ParseExact(date, originalFormat, CultureInfo.InvariantCulture);

            // 원하는 형식으로 변환
            string targetFormat = "yyyy-MM-dd tt h:mm";
            string result = dateTime.ToString(targetFormat, CultureInfo.InvariantCulture);


            // 오전/오후 표시를 한글로 변경
            date = result.Replace("AM", "오전").Replace("PM", "오후");

            ListViewItem lsvitem = new ListViewItem();
            lsvitem.Text = name;
            listView2.Items.Add(lsvitem);
            listView2.Items[DirectCount].SubItems.Add(date);
            if (parts[2] == "<DIR>")
            {
                listView2.Items[DirectCount].SubItems.Add("폴더");
            }
            else
            {
                listView2.Items[DirectCount].SubItems.Add("파일");
            }
            listView2.Items[DirectCount].SubItems.Add("");
            DirectCount++;
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("에러 발생 : " + ex.Message);
    }
    ftpDirectory.Nodes[0].Expand();
}

 


 

Drag & Drop 기능은 너무 길고 여기에 사용되지 않은 메서드들과 연관이 되어 있어서 github를 통해 확인해 보면 좋을 거 같다. 

 

https://github.com/7jjin/FTP_Project