標籤:標識 .dll 命令列 www. manual 文章 war com 資料
最近做的一個項目,使用小工具代替人工提交到網站並擷取結果。
在開發過程中碰到了一個問題。
網站返回的格式為tar.gz檔案。第一個想到的方式是直接用第三方工具,使用的是7z。但使用者那裡使用的系統為win10,不以管理員身份運行會沒有調用第三方工具的許可權。遂決定用庫解決該問題。
這裡記錄下windows下解壓tar.gz的一些問題。
其實tar.gz是兩次封裝。
第一次是tar打包(這裡不會壓縮,檔案反而會更大),第二次是gz檔案的演算法壓縮。
解壓GZ檔案使用的是zlib庫。
這裡說一下zlib的使用過程。
一開始我是圖懶直接從官網下載的windows develop版本,包含標頭檔,靜態庫和dll。但運行過程中出現了缺少zlib1.dll的問題。經過反覆的折騰,沒有解決。他這個庫產生時間是2005年,可能在新版windows下運行會有問題把。
本人對系統瞭解還是不多,沒有找到深層次的原因。如果有大神知道原理請告知。
在此之後下載了新版並進行編譯。具體編譯過程忘記了,挺順利的。在vs命令列裡執行一條命令後,會直接產生vs工程項目。選擇靜態庫或動態庫編譯即可。
不知道這裡能不能發csdn的串連,我是根據這篇文章來編譯的:http://blog.csdn.net/shellching/article/details/8116622。
當然我用的版本是vs2015。用這種方法也能順利通過。
接下來就是解壓了。一開始我從網上找到的資料全是compress和uncompress,後來發現:
錯啦!
壓根就不是這麼解壓的。
具體原因沒有詳細瞭解到,大體上我的理解就是,其實gz檔案需要一個頭來標識壓縮層級等資訊。而compress和uncompress只是壓縮位元組流,單純地調用uncompress而不知道資料的開始位置和資料的壓縮資訊的話,是無法正常壓縮的。
下面是解壓代碼:
//初始化結構體
z_stream strm = { 0 };
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
//下面分別初始化來源資料,來源資料長度,目標資料長度和目標資料地址
strm.next_in = srcBuffer;
strm.avail_in = srcLength;
strm.avail_out = dstLength;
strm.next_out = dstBuffer;
//下面函數的第二個參數在標頭檔的函數說明裡有,這裡的這個參數意思是自動判斷資訊頭
int res = inflateInit2(&strm, MAX_WBITS + 32);
//執行解壓縮
res = inflate(&strm, Z_NO_FLUSH);
//回收資源
inflateEnd(&strm);
上面的代碼我去掉了結果判斷的部分,具體的結果判斷大家參考標頭檔的資訊把。我其實在代碼裡沒有進行詳細判斷,只是簡單判斷了成功失敗,並沒有返回原因。這裡就不貼上來坑大家了。
有幾點注意事項:
1,目標記憶體長度需要足夠。當然不夠也沒關係,該函數會在執行時發現並返回這個錯誤,在判斷後逐步增加緩衝區即可。
2,轉換成功後目標資料的長度為:dstLen - strm.avail_out。
3,如果你編譯後使用這些函數,這個程式會莫名其妙地崩掉。
4,如果你是新手(現在沒啥新手在VS上用這個了把。。。),可能會報無法串連的錯誤,當然你引入了lib還是會報。
先說下為何會報串連錯誤,需要加一個宏ZLIB_WINAPI,如果有新手在看的話,跟大家分享下。
一個庫,無論是靜態還是動態。在有標頭檔的時候,有兩個地方需要注意,一是你編譯庫的時候,匯出函數前面有沒有加export,第二個是你引入庫的時候,匯出函數前面是不是import。
有些庫因為要處理跨平台的問題,庫的源碼的宏定義會非常複雜,這時候要仔細觀察,設定響應的宏確定庫函數的正確引入和匯出,如果你用VS的話,VS會根據宏定義的狀態來高亮具體代碼用到哪個宏。
在編譯和引入的時候都要注意。
接下來,在崩潰之後,各種google原因,但始終沒有發現是為什麼,因為當時沒有解壓成功,我也一度懷疑自己的代碼有問題,或者庫有問題。
在這裡,感謝一位大神:http://blog.csdn.net/u013283835/article/details/70311499
真是跪謝。這是怎麼發現的。祝您能修到孫藝珍。
根據這位大神的說法,把前置處理器中的ASMINF這個宏去掉。執行程式將轉換出來的位元組流寫入檔案。搞定了。
這裡鄙視下CSDN:本人的csdn因為前幾年給別人發QSS代碼被封了。我也是醉了,發個QSS代碼都能封。就這還搞技術論壇。呵呵。
接下來就進行tar檔案的解析了。最開始想到的使用libarchive庫。
嗯。編譯,串連,匯入,使用。在經曆過種種挫折後,我突然發現:libarchive不支援全路徑,只能開啟當前工作目錄下的檔案,當然也可能是我某個配置沒有配好,但libarchive的資料實在是太少了。
官方文檔也只有很粗略的解釋。而且在win10下,如果程式安裝在C盤program files下,運行程式是沒有建立檔案許可權的,在其他盤則需要不停設定當前路徑。放棄。這個庫真不是一般的難用。
查了下tar檔案的格式:
http://blog.chinaunix.net/uid-20357359-id-1963469.html
https://www.gnu.org/software/tar/manual/html_node/Standard.html
嗯,貌似做簡單的拆包不複雜嗎。外加我做的小項目中tar檔案本就是自動產生的,只有一級目錄。自己解決把。
下面是代碼:
typedef struct posix_header
{
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag;
char linkname[100];
char magic[6];
char version[2];
char uname[32];
char gname[32];
char devmajor[8];
char devminor[8];
char prefix[155];
}TAR_HEADER;
//參數為目標路徑、tar緩衝區,緩衝區長度
void ExtractTar(const char *dstDir, BYTE *buffer, uLongf len){ uLongf index = 0; while (true) { if (index == len - 1) { return; } TAR_HEADER newHeader = { 0 }; memcpy(&newHeader, buffer + index, sizeof(TAR_HEADER)); index += sizeof(TAR_HEADER); if (strcmp(newHeader.name, "") == 0) { return; } char fileFullPath[MAX_PATH] = { 0 }; memcpy(fileFullPath, dstDir, strlen(dstDir)); memcpy(fileFullPath + strlen(dstDir), newHeader.name, strlen(newHeader.name)); FILE *newFile = NULL; fopen_s(&newFile, fileFullPath, "wb+");long size = strtol(newHeader.size, (char **)(&(newHeader.size) + 12), 8); size_t res = fwrite(buffer + index, 1, size, newFile); fclose(newFile); index += size;
//填充位元組塊 int nblock = index / 512 + 1; index = nblock * 512; }}
注意:
1,我還是去掉了返回判斷的代碼,如果想完成健壯代碼請判斷各種狀態傳回值。
2,這段代碼不能解壓包含檔案夾或者多級目錄的tar檔案,也不能解壓特殊檔案,也沒有判斷位元組位移等資料。
簡單來說,這段代碼只能保證我這個小項目的需求。而且很大程度上滿足不了其他需求。
經過上面兩步,基本上滿足了我的需求。在這裡分享給大家。
windows下解壓tar.gz檔案