利用 MFC WinInet 類別(CInternetSession、CInternetFile、CStdioFile)即可輕鬆實現「輸入 URL → 下載 HTML → 儲存檔案」的完整流程。本文封裝通用函式 GetSourceHtml,處理例外、編碼、大檔分塊寫入,並示範進度顯示與編碼轉換技巧。
BOOL GetSourceHtml(const CString& strUrl, const CString& strSavePath)
{
CInternetSession session(_T("MyDownloader"), 1,
PRE_CONFIG_INTERNET_ACCESS, NULL, NULL, 0);
CInternetFile* pFile = NULL;
try {
// 1. 開啟 URL (支援 HTTP/HTTPS/FTP)
pFile = (CInternetFile*)session.OpenURL(strUrl, 1,
INTERNET_FLAG_RELOAD | INTERNET_FLAG_DONT_CACHE);
}
catch (CInternetException* pEx) {
pEx->GetErrorMessage(m_strError.GetBuffer(256), 256);
m_strError.ReleaseBuffer();
pEx->Delete();
return FALSE;
}
if (!pFile) return FALSE;
// 2. 開啟本地檔案 (文字模式自動處理換行)
CStdioFile fileLocal;
if (!fileLocal.Open(strSavePath,
CFile::modeCreate | CFile::modeWrite | CFile::typeText)) {
delete pFile;
return FALSE;
}
// 3. 迴圈讀寫 (ReadString 自動處理緩衝)
CString strLine;
while (pFile->ReadString(strLine)) {
fileLocal.WriteString(strLine + _T("\n")); // 保留換行
}
// 4. 清理
fileLocal.Close();
delete pFile;
session.Close();
return TRUE;
}
dwContext:非同步回調識別碼,同步模式傳 1 即可。dwAccessType:PRE_CONFIG_INTERNET_ACCESS 自動使用系統 Proxy 設定(IE/Edge 設定)。OpenURL 旗標:INTERNET_FLAG_RELOAD 強制重新下載,INTERNET_FLAG_DONT_CACHE 避免快取污染。網頁多為 UTF-8,MFC CString (Unicode 建置) 需轉換:
// 讀取原始位元組
CByteArray rawData;
BYTE buf[4096];
UINT nRead;
while ((nRead = pFile->Read(buf, 4096)) > 0) {
rawData.Append(buf, nRead);
}
// UTF-8 轉 Unicode
int nChars = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)rawData.GetData(),
rawData.GetSize(), NULL, 0);
CStringW unicodeStr;
MultiByteToWideChar(CP_UTF8, 0, (LPCCH)rawData.GetData(),
rawData.GetSize(), unicodeStr.GetBuffer(nChars), nChars);
unicodeStr.ReleaseBuffer();
// 寫入 Unicode 文字檔
CStdioFile file(strSavePath, CFile::modeCreate|CFile::modeWrite|CFile::typeText);
file.WriteString(unicodeStr);
// 取得 Content-Length
CString strLen;
pFile->QueryInfo(HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &strLen);
ULONGLONG total = _ttoi64(strLen);
ULONGLONG downloaded = 0;
BYTE buf[8192];
while ((nRead = pFile->Read(buf, 8192)) > 0) {
fileLocal.Write(buf, nRead); // 二進位寫入
downloaded += nRead;
int percent = total ? (int)(downloaded * 100 / total) : 0;
pDlg->PostMessage(WM_DOWNLOAD_PROGRESS, percent, 0);
}
catch (CInternetException* pEx) {
TCHAR szErr[256];
pEx->GetErrorMessage(szErr, 255);
TRACE(_T("下載失敗: %s (Error 0x%X)\n"), szErr, pEx->m_dwError);
pEx->Delete();
return FALSE;
}
catch (CFileException* pEx) {
// 本地檔案寫入錯誤 (磁碟滿、權限...)
pEx->Delete();
return FALSE;
}
catch (CMemoryException* pEx) {
// 記憶體不足
pEx->Delete();
return FALSE;
}
class CHtmlDownloader {
public:
// 同步下載
static BOOL Download(LPCTSTR url, LPCTSTR savePath,
CWnd* pProgressWnd = NULL, UINT msgProgress = 0);
// 非同步下載 (工作者執行緒)
static UINT AFX_CDECL AsyncDownload(LPVOID pParam);
struct AsyncParam {
CString url, savePath;
CWnd* pWnd; UINT msg;
BOOL bResult; CString error;
};
};
| 場景 | 關鍵設定 |
|---|---|
| 離線瀏覽/歸檔 | 遞迴解析 HTML 中的 href/src,批次下載資源 |
| 網頁異動監控 | 定時下載、計算雜湊值比對、差異通知 |
| 資料擷取/爬蟲 | 搭配正則表達式/HTML Parser (如 Gumbo) 提取結構化資料 |
| 軟體自動更新 | 下載版本資訊 XML/JSON,比對後下載安裝包 |