首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

WinInet CFtpFileFind 遞迴遍歷 FTP 目錄完整實作

     編輯:關於VC++

WinInet 封裝了 Winsock 細節,提供 CInternetSession、CFtpConnection、CFtpFileFind 等類別,大幅簡化 FTP 用戶端開發。本文示範如何結合遞迴演算法遍歷 FTP 伺服器完整目錄樹,並以縮排顯示層級結構,同時解決 CFtpFileFind 不支援並行實例的限制。

核心類別與流程

  • CInternetSession:初始化網際網路會話
  • CFtpConnection:建立 FTP 連線(主機、連接埠、帳號、密碼)
  • CFtpFileFind:列舉檔案/目錄(同一時間僅能存在一個實例)

連線與錯誤處理


m_pInetSession = new CInternetSession(AfxGetAppName(), 1, PRE_CONFIG_INTERNET_ACCESS);
try {
    m_pFtpConnection = m_pInetSession->GetFtpConnection(m_host, m_username, m_password, m_port);
    if (m_pFtpConnection) {
        n = 0;  // 層級縮排計數器
        List(); // 遞迴遍歷入口
    }
} catch (CInternetException* pEx) {
    TCHAR szErr[1024];
    pEx->GetErrorMessage(szErr, 1024);
    // 顯示錯誤...
    pEx->Delete();
}

遞迴遍歷核心演算法


void CMyDlg::List()
{
    CFtpFileFind finder(m_pFtpConnection);
    CStringArray dirs;  // 暫存子目錄名稱
    BOOL bWorking = finder.FindFile(_T("*"));

    // 第一輪:區分檔案與目錄
    while (bWorking) {
        bWorking = finder.FindNextFile();
        if (finder.IsDots()) continue;

        if (finder.IsDirectory()) {
            dirs.Add(finder.GetFileName());  // 目錄暫存,稍後遞迴
        } else {
            // 顯示檔案(含層級縮排)
            CString indent;
            for (int j = 0; j < n; j++) indent += _T("\t");
            m_ftpInfo += indent + finder.GetFileName() + _T("\r\n");
            UpdateData(FALSE);
        }
    }
    finder.Close();  // 必須關閉,釋放連線控制權

    // 第二輪:遞迴進入子目錄
    for (int i = 0; i < dirs.GetSize(); i++) {
        n++;  // 增加縮排層級
        CString dirName = _T("[") + dirs[i] + _T("]\r\n");
        CString indent;
        for (int j = 1; j < n; j++) indent += _T("\t");
        m_ftpInfo += indent + dirName;
        UpdateData(FALSE);

        // 切換工作目錄
        while (!m_pFtpConnection->SetCurrentDirectory(dirs[i])) { /* 重試 */ }

        List();  // 遞迴呼叫

        // 返回上層目錄
        while (!m_pFtpConnection->SetCurrentDirectory(_T(".."))) { /* 重試 */ }
        n--;
    }
}

關鍵限制與解決方案

問題:CFtpFileFind 同一連線同一時間只能有一個作用中實例。若在遞迴呼叫 List() 時外層 finder 尚未 Close(),將觸發 ERROR_FTP_TRANSFER_IN_PROGRESS。

解法:第一輪掃描時將子目錄名稱收集至 CStringArray,finder.Close() 釋放控制權後,再逐一進入子目錄遞迴。此為「深度優先、串行化」策略。

改進方向

  • 執行緒化:遍歷耗時長,建議移至背景執行緒(AfxBeginThread),避免 UI 凍結
  • 非遞迴實作:使用显式堆疊避免深層目錄導致堆疊溢位
  • 異步通知:配合 CAsyncSocket 或 WinInet 非同步模式提升響應性
  • 錯誤重試與超時:網路不穩時加入指數退避重試機制

替代實作(網友提供)


void ExploreDir(CString strDir) {
    CFtpFileFind finder(pFtp);
    CStringList list;
    pFtp->SetCurrentDirectory(strDir);
    BOOL bContinue = finder.FindFile(_T("*"));
    while (bContinue) {
        bContinue = finder.FindNextFile();
        CString name = finder.GetFileName();
        if (name == _T(".") || name == _T("..")) continue;
        if (finder.IsDirectory()) list.AddTail(name);
    }
    finder.Close();
    while (!list.IsEmpty()) {
        ExploreDir(list.RemoveHead());
    }
}

此版本同樣遵循「掃描收集 → 關閉 → 遞迴」模式,使用 CStringList 替代陣列。

總結

WinInet 提供高層級 FTP 操作介面,配合遞迴演算法可輕鬆實現目錄樹遍歷。關鍵在於理解 CFtpFileFind 的單實例限制,並採用「兩階段掃描」策略繞過此限制。實際部署時建議加入執行緒隔離、進度回報與錯誤恢復機制,構建健壯的 FTP 管理工具。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved