本文將介紹如何實現一個離線浏覽器,以下載並浏覽網上資源。
鏡像目錄結構
離線浏覽下載到本地的網頁時,需要解決的一個關鍵性問題就是“如何通過某一網頁中的超級鏈接正確地定位其他網頁”。比較簡便的方法是在用戶指定的本地目錄下建立一個目標網站的完整或部分鏡像。也就是按照文件在服務器端的目錄結構保存下載的文件(參見下圖)。這樣一來,如果網頁中的超級鏈接是以相對路徑形式給出的,那麼浏覽程序就可以直接通過此相對路徑訪問到本地文件系統中的網頁;如果網頁中的超級鏈接是以絕對的URL形式給出的,那麼必須在保存網頁之前將這些URL轉換為本地絕對路徑。
在網絡中,一個有效的URL應該只有唯一的網絡文件與之對應。因此,只要將網絡上由URL所確定的層次關系,轉化為本地文件系統中由目錄路徑所確定的層次關系,就可以建立網站在本地的完全或部分鏡像。下面討論建立鏡像的具體方法。
鏡像路徑算法
首先,將下載網頁時生成的URL拆分成協議類名(protocol)、IP地址(ipaddr)、目錄名(directory)和文件名(file)。
KDE環境提供了一個用於解析URL的類KURL,只需要定義一個對象KURL u((const char*)URL),就可以利用該類提供的成員函數將URL拆解為所需的部分。但是,此類未提供對ASP定位語句的支持,所以讀者可以在KURL的基礎上編寫自己的拆解函數,以完善程序功能。
需要注意的是,在同一網絡文件的URL中,網址部分可能是以域名地址形式給出的,也可能是以IP 地址形式給出的。為了避免將同一文件鏡像到不同目錄下,如果網址是域名形式的,應該使用socket函數gethostbyname ()將其轉換為IP地址。
其次,確定網絡文件在本地的鏡像路徑。假設用戶指定的本地目錄存放在字符數組LDir中,則代碼如下:
QString LocalDir = LDir + “/” + protocol + “_” + ipaddr + directory
QString LocalPath = LocalDir + file;
這樣一來,如果一個網絡文件的URL是http://11.171.38.32/webfile/relax/index.html,而用戶指定的本地目錄是/home/yangjx/web,則此網頁文件對應的鏡像路徑為/home/yangjx/web/http_11.171.38.32/webfile/relax/index.html。
處理下載文件
有了鏡像路徑生成算法,接下來要對下載的文件做如下處理:
● 如果是網頁文件,必須掃描文件,並將其中以絕對URL形式給出的超級鏈接替換成用鏡像路徑生成算法產生的本地絕對路徑,而那些以相對路徑形式給出的超級鏈接則保持不變;
● 建立相應的目錄,並保存文件到絕對路徑所指定的位置。
在建立目錄時,由於Linux提供的目錄創建函數int mkdir(char * dir, int mode)只能在已存在的目錄下建立一級子目錄,所以要用“遞歸”方式構造一個目錄創建函數:
static int CDirTools:: Mkdir(QString dir,int mode);
{;
QString parentdir
if(dir.isEmpty());
//如果dir為空串返回失敗;
return -1
int result = mkdir(dir,mode)
if(result == -1 && errno == EEXIST) //如果dir目錄已經存在,則返回1;
return 1
if(result != -1);
//如果建立成功,則返回0;
return 0
else;
{//否則先創建其父目錄;
KURL u((const char *)dir)
//取得dir的父目錄 ;
parentdir = url.directory(false)
if(Mkdir(parentdir) == -1);
//如果父目錄創建失敗,則返回-1;否則再次創建本目錄;
return -1
if(mkdir(dir,mode) == -1);
//如果本目錄創建失敗,則返回-1;
return -1
};
}
編程實現
Linux操作系統的桌面環境KDE提供了一個文件管理器KFM,它和IE一樣既可以浏覽本地目錄和文件,也可以浏覽網頁,並且KFM還提供了C++編程接口: KHTMLView類。我們可以創建一個KHTMLView類的子類CHtmlView來浏覽下載的網頁文件。
1.在窗口中顯示HTML頁面
int CHtmlView:: showPage(const char * path);
{ //顯示path指定的文件中所包含的HTML頁面;
if(path == NULL);
return -1
else;
{;
FILE * pfile
//打開包含頁面的文件;
if((pfile = fopen((const char*)path,“rb”)) != NULL);
{;
int blocklen = 0x10000
char * c = new char[blocklen+1]
KURL u((const char*)path)
//類成員函數,清除窗口內原有內容,並初始化窗口,准備顯示新頁面;
begin( u.directoryURL() )
while(1);
{;
//讀出網頁文件的內容;
int len = fread(c,sizeof(char),blocklen,pfile)
//類成員函數,將讀取的內容寫入KHTMLView類的緩沖區;
write(c)
//文件讀取完畢後退出循環;
if(len < blocklen);
break
};
//類成員函數,標示HTML頁面已經全部寫入緩沖區;
end()
//類成員函數,分析緩沖區中的HTML代碼;
parse()
//類成員函數,顯示HTML頁面;
show()
delete [] c
};
else return -1
};
return 0
}
2.響應超級鏈接的點擊
定義鼠標事件處理函數mousePressedHook()覆蓋KHTMLView類中的同型虛擬函數。當用戶用鼠標點擊網頁中的超級鏈接時,該函數將被調用。被點擊的超級鏈接的地址會作為參數自動傳入該函數。由於網頁文件中的所有超級鏈接已做過本地鏡像處理,所以,只要該鏈接所指向的文件已經被下載程序正確地下載到本地,那麼使用showPage函數就能調入並顯示此頁。
bool CHtmlView:: mousePressedHook;
( const char* _url, const char *_target,;
QMouseEvent *_ev, bool _isselected );
{;
KHTMLView:: mousePressedHook(_url,_target,_ev,_isselected)
//顯示被點擊的頁面;
showPage(_url)
return true
}
在生成Kdevelop的窗口應用程序框架的View類中定義一個ChtmlView對象,將View類作為其父窗口:
ChtmlView *m_htmlview = new ChtmlView(this,“HtmlViewer”)
/*調用showPage函數顯示path指向的網頁文件*/;
m_htmlview-> showPage(path);
此外,我們還可以在此基礎上加入更多的功能,依靠KDevelop所提供的豐富的圖形用戶接口類將浏覽器設計得更美觀易用。