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

MFC 實現 FTP 客戶端核心功能 (CInternetSession/CFtpConnection 完整指南)

     編輯:VC 教學

前言

MFC WinInet 封裝類 (CInternetSession, CFtpConnection, CInternetFile) 提供了極簡單的 FTP 客戶端開發接口。本文系統講解 連接、上傳/下載、目錄列表、斷點續傳、錯誤處理 等核心功能實現。

一、基礎架構與對象關係

CInternetSession (會話管理、代理設置、超時控制)
    → GetFtpConnection() → CFtpConnection (FTP 連接、命令通道)
        → OpenFile() → CInternetFile (數據通道、讀寫流)
        → GetFile() / PutFile() (高層封裝,單文件傳輸)
        → CFtpFileFind (目錄遍歷)

二、核心操作代碼實現

1. 建立會話與連接

CInternetSession session(_T("MyFtpClient/1.0"), 1, PRE_CONFIG_INTERNET_ACCESS); // 1=INTERNET_OPEN_TYPE_DIRECT

// 連接參數:服務器、用戶、密碼、端口(默認21)、被動模式(TRUE)
CFtpConnection* pFtp = session.GetFtpConnection(
    _T("ftp.microsoft.com"), 
    _T("anonymous"), 
    _T("user@domain.com"), // 匿名登錄常用郵箱作密碼
    21, 
    TRUE // 被動模式 (PASV),穿透防火牆/NAT 必須
);

if (!pFtp) {
    DWORD err = GetLastError(); // ERROR_INTERNET_...
    // 處理錯誤
}

2. 簡單文件下載/上傳 (高層 API)

// 下載:遠程文件 → 本地文件 (自動處理類型、斷點不支持)
BOOL bOk = pFtp->GetFile(
    _T("/pub/README.txt"),   // 遠程路徑
    _T("C:\\Temp\\README.txt"), // 本地路徑
    FALSE,                   // bFailIfExists: FALSE=覆蓋
    FILE_ATTRIBUTE_NORMAL,   // 屬性
    FTP_TRANSFER_TYPE_BINARY, // 二進制模式 (關鍵!ASCII 模式會損壞二進制文件)
    0                        // dwContext (異步用)
);

// 上傳:本地文件 → 遠程文件
bOk = pFtp->PutFile(
    _T("C:\\Data\\data.zip"),
    _T("/upload/data.zip"),
    FTP_TRANSFER_TYPE_BINARY,
    0
);

3. 低層流式讀寫 (支持大文件、進度條、斷點續傳)

// 打開遠程文件讀取 (下載)
CInternetFile* pFile = pFtp->OpenFile(_T("/bigfile.iso"), GENERIC_READ, FTP_TRANSFER_TYPE_BINARY);
if (pFile) {
    // 斷點續傳:設置文件指針
    ULONGLONG ullResumePos = 1024 * 1024 * 100; // 100MB
    pFile->Seek(ullResumePos, CFile::begin); // 需服務器支持 REST 命令

    // 本地文件
    CFile localFile(_T("D:\\bigfile.iso"), CFile::modeCreate | CFile::modeWrite | CFile::typeBinary);
    localFile.Seek(ullResumePos, CFile::begin);

    // 循環讀寫
    const int BUFSIZE = 32768;
    BYTE buffer[BUFSIZE];
    UINT nRead;
    while ((nRead = pFile->Read(buffer, BUFSIZE)) > 0) {
        localFile.Write(buffer, nRead);
        // 更新進度...
    }
    localFile.Close();
    pFile->Close();
    delete pFile; // OpenFile 返回指針需手動刪除
}

// 打開遠程文件寫入 (上傳/續傳)
CInternetFile* pPutFile = pFtp->OpenFile(_T("/upload/bigfile.iso"), GENERIC_WRITE, FTP_TRANSFER_TYPE_BINARY);
// 同理 Seek + Write 循環

4. 目錄列表與文件查找 (CFtpFileFind)

CFtpFileFind finder(pFtp);
BOOL bFound = finder.FindFile(_T("*.*")); // 根目錄
while (bFound) {
    bFound = finder.FindNextFile();
    if (finder.IsDots()) continue; // 跳過 . 和 ..
    
    CString name = finder.GetFileName();
    BOOL isDir = finder.IsDirectory();
    ULONGLONG size = finder.GetLength(); // 文件大小
    CTime time; finder.GetLastWriteTime(time); // 修改時間
    
    // 處理條目...
    // 遞歸進入子目錄: pFtp->SetCurrentDirectory(name); // 遞歸調用 FindFile
}
finder.Close();

5. 目錄操作

pFtp->CreateDirectory(_T("new_folder"));
pFtp->RemoveDirectory(_T("empty_folder")); // 必須為空
pFtp->Remove(_T("file_to_delete.txt"));
pFtp->Rename(_T("old_name.txt"), _T("new_name.txt"));
pFtp->SetCurrentDirectory(_T("subdir")); // 切換工作目錄
CString curDir; pFtp->GetCurrentDirectory(curDir); // 獲取當前目錄

三、異步操作與進度回調 (進階)

CInternetSession 構造函數第 3 參數傳 AFX_INET_ASYNC (或設置 EnableStatusCallback(TRUE)),重寫 OnStatusCallback:

class CMyFtpSession : public CInternetSession {
public:
    CMyFtpSession() : CInternetSession(_T("MyFtp"), 1, AFX_INET_ASYNC) {
        EnableStatusCallback(TRUE);
    }
protected:
    virtual void OnStatusCallback(DWORD dwContext, DWORD dwInternetStatus, 
                                  LPVOID lpvStatusInformation, DWORD dwStatusInformationLength) {
        switch (dwInternetStatus) {
            case INTERNET_STATUS_RECEIVING_RESPONSE:
            case INTERNET_STATUS_RESPONSE_RECEIVED:
            case INTERNET_STATUS_SENDING_REQUEST:
            case INTERNET_STATUS_REQUEST_SENT:
            case INTERNET_STATUS_DATA_AVAILABLE: // lpvStatusInformation = 可用字節數
                // 更新 UI 進度條...
                break;
            case INTERNET_STATUS_REQUEST_COMPLETE:
                // INTERNET_ASYNC_RESULT* pResult = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
                // 檢查 pResult->dwError / dwResult
                break;
        }
    }
};

四、錯誤處理與常見錯誤碼

統一錯誤處理模式:

try {
    // FTP 操作...
} catch (CInternetException* pEx) {
    TCHAR szErr[256];
    pEx->GetErrorMessage(szErr, 255);
    AfxMessageBox(szErr);
    pEx->Delete();
}

五、最佳實踐清單

  1. 強制二進制模式:FTP_TRANSFER_TYPE_BINARY (默認 ASCII 會破壞圖片/壓縮包/可執行文件)。
  2. 被動模式 (PASV):GetFtpConnection(..., TRUE),客戶端發起數據連接,適應 NAT/防火牆。
  3. 資源嚴格釋放:CInternetFile* 需 Close() 並 delete;CFtpConnection* 由 CInternetSession 析構時自動清理,或手動 delete。
  4. 異常捕獲:所有 WinInet 操作拋 CInternetException,必須 catch 並 Delete()。
  5. 大文件/斷點續傳:用 OpenFile + Seek + 循環 Read/Write,捕獲 CInternetException 判斷服務器是否支持 REST。
  6. 編碼問題:中文文件名/路徑在 FTP 協議層面無統一編碼標準,建議雙方約定 UTF-8 並手動 WideCharToMultiByte(CP_UTF8) / MultiByteToWideChar。

結語

MFC CFtpConnection 封裝了 WinInet FTP 協議的複雜性,以 GetFile/PutFile 滿足簡單需求,以 OpenFile/CFtpFileFind 支持複雜場景。掌握「會話→連接→文件/查找」對象鏈、二進制/被動模式強制、異常處理、資源釋放,即可快速構建穩健的 FTP 客戶端功能。若需 FTPS (SSL/TLS)、SFTP (SSH) 或高併發異步傳輸,建議引入 libcurl、OpenSSL 或 libssh2 等專業庫。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
錯誤碼含義排查方向
ERROR_INTERNET_INVALID_URLURL 格式錯誤檢查服務器地址、路徑編碼
ERROR_INTERNET_CANNOT_CONNECT無法連接服務器IP/端口、防火牆、服務器運行狀態
ERROR_INTERNET_LOGIN_FAILURE用戶名/密碼錯誤憑證、匿名登錄策略
ERROR_INTERNET_ACCESS_DENIED權限不足文件/目錄權限、用戶組
ERROR_INTERNET_OPERATION_CANCELLED操作取消/超時超時設置、用戶取消
ERROR_FTP_TRANSFER_IN_PROGRESS已有傳輸進行中同一連接不支持並發數據通道