源代码略解
主要讲下zextract工程好了,其实只是实现了3个函数,并且只有两个还是靠谱的,其中只有一个是实用的。分别是1.『列出压缩包内的文件』,2.『查找压缩包中文件』,3.『获取压缩包内的文件』。第一个明显是玩玩的,因为直接是printf输出到console中,也是参考了minizip中的do_list函数的实现。第二个函数写是写了,但是没啥大用,以玩玩为主。最后一个是有用的东西,实现了从压缩包中读取文件的功能,其中还用到了CMemBuffer类。先看下工程的头文件好了。
- // The following ifdef block is the standard way of creating macros which make exporting
- // from a DLL simpler. All files within this DLL are compiled with the ZEXTRACT_EXPORTS
- // symbol defined on the command line. this symbol should not be defined on any project
- // that uses this DLL. This way any other project whose source files include this file see
- // ZEXTRACT_API functions as being imported from a DLL, wheras this DLL sees symbols
- // defined with this macro as being exported.
- #ifdef ZEXTRACT_EXPORTS
- #define ZEXTRACT_API __declspec(dllexport)
- #else
- #define ZEXTRACT_API __declspec(dllimport)
- #endif
- #include "MemBuffer.h"
- #include "luna.hpp"
- ZEXTRACT_API void ListZip(constchar* fname);
- ZEXTRACT_API bool FindFileInZip(constchar* zfn, constchar* fname);
- ZEXTRACT_API int GetFileInZip(CMemBuffer& buffer,const char* zfn,const char* fname,const char* password);
- extern "C" ZEXTRACT_APIint luaopen_zextract(lua_State* L);
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the ZEXTRACT_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// ZEXTRACT_API functions as being imported from a DLL, wheras this DLL sees symbols
// defined with this macro as being exported.
#ifdef ZEXTRACT_EXPORTS
#define ZEXTRACT_API __declspec(dllexport)
#else
#define ZEXTRACT_API __declspec(dllimport)
#endif
#include "MemBuffer.h"
#include "luna.hpp"
ZEXTRACT_API void ListZip(const char* fname);
ZEXTRACT_API bool FindFileInZip(const char* zfn, const char* fname);
ZEXTRACT_API int GetFileInZip(CMemBuffer& buffer, const char* zfn, const char* fname, const char* password);
extern "C" ZEXTRACT_API int luaopen_zextract(lua_State* L);
讲下
GetFileInZip函数。一共有4个入参:
- CMemBuffer对象,用来存储从压缩包中抽取的文件。
- 对应的ZIP Archive文件路径,相对路径和绝对路径皆可。
- 对应在ZIP Archive中要提取的文件路径。
- 密码,如果没有密码则填NULL或者0即可。
函数返回大于等于0则表示提取的文件的大小,如果返回小于0,则是对应的错误代码。如果执行成功,则文件内容存放在buffer对象中。
其余两个函数没什么介绍的必要,自己看下源代码即可。
zextract.cpp::GetFileInZip
- ZEXTRACT_API int GetFileInZip(CMemBuffer& buffer,const char* zfn,const char* fname,const char* password)
- {
- unzFile uf = unzOpen(zfn);
- if (NULL == uf)
- {
- printf("unzOpen failed...\n");
- return -1;
- }
- int err = unzLocateFile(uf, fname, 0);
- if (UNZ_OK != err)
- {
- printf("GetFileInZip unzLocateFile failed... error:%d\n");
- return err;
- }
- unz_file_info file_info;
- char filename_inzip[256];
- err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip,sizeof(filename_inzip), NULL, 0, NULL, 0);
- if (UNZ_OK != err)
- {
- printf("unzGetCurrentFileInfo failed... error:%d\n", err);
- return err;
- }
- err = unzOpenCurrentFilePassword(uf, password);
- if (UNZ_OK != err)
- {
- printf("unzOpenCurrentFilePassword failed... error:%d\n", err);
- return err;
- }
- char* pBuff = newchar[file_info.uncompressed_size];
- if (pBuff == NULL)
- {
- unzCloseCurrentFile(uf);
- unzClose(uf);
- return -2;
- }
- err = unzReadCurrentFile(uf, pBuff, file_info.uncompressed_size);
- if (err < 0)
- {
- printf("unzReadCurrentFile failed... error:%d\n", err);
- delete [] pBuff;
- unzCloseCurrentFile(uf);
- unzClose(uf);
- return err;
- }
- // Append data to the MemBuffer
- buffer.Append(pBuff, file_info.uncompressed_size);
- unzCloseCurrentFile(uf);
- unzClose(uf);
- return err;
- }
ZEXTRACT_API int GetFileInZip(CMemBuffer& buffer, const char* zfn, const char* fname, const char* password)
{
unzFile uf = unzOpen(zfn);
if (NULL == uf)
{
printf("unzOpen failed...\n");
return -1;
}
int err = unzLocateFile(uf, fname, 0);
if (UNZ_OK != err)
{
printf("GetFileInZip unzLocateFile failed... error:%d\n");
return err;
}
unz_file_info file_info;
char filename_inzip[256];
err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
if (UNZ_OK != err)
{
printf("unzGetCurrentFileInfo failed... error:%d\n", err);
return err;
}
err = unzOpenCurrentFilePassword(uf, password);
if (UNZ_OK != err)
{
printf("unzOpenCurrentFilePassword failed... error:%d\n", err);
return err;
}
char* pBuff = new char[file_info.uncompressed_size];
if (pBuff == NULL)
{
unzCloseCurrentFile(uf);
unzClose(uf);
return -2;
}
err = unzReadCurrentFile(uf, pBuff, file_info.uncompressed_size);
if (err < 0)
{
printf("unzReadCurrentFile failed... error:%d\n", err);
delete [] pBuff;
unzCloseCurrentFile(uf);
unzClose(uf);
return err;
}
// Append data to the MemBuffer
buffer.Append(pBuff, file_info.uncompressed_size);
unzCloseCurrentFile(uf);
unzClose(uf);
return err;
}
其中关键的操作函数就只有7个:
unzOpen
| 打开Archive文件 |
unzClose | 关闭Archive文件 |
unzGetCurrentFileInfo | 获取当前选择的内部压缩文件的信息 |
unzLocateFile | 定位文件 |
unzOpenCurrentFilePassword | 选择打开当前文件 |
unzReadCurrentFile | 读取当前文件 |
unzCloseCurrentFile | 关闭当前文件 |
其中后三个是需要组合使用的,也就是说需要读取一个内部文件,首先要打开,然后才是读取,最后要关闭。而选择当前文件的方法有很多,这里使用
unzLocateFile函数实现的,如果找到对应文件会把这个文件默认选中为当前文件。
ListZip函数的实现中还有遍历的代码可以参考。
CMemBuffer
CMemBuffer类其实只是实现了一个存放内存数据的功能。考虑使用一个类专门来存储文件数据是有多方面的考虑,比如文件是二进制的——图片,还有就是获取数据是从堆上申请的内存,要考虑自己释放的问题。自己手动去管理明显会很麻烦,不如专门搞个缓存类来管理这些内存数据块。
- // MemBuffer.h: interface for the CMemBuffer class.
- //
- //
- #if !defined(AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_)
- #define AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_
- #if _MSC_VER > 1000
- #pragma once
- #endif // _MSC_VER > 1000
- class CMemBuffer
- {
- public:
- CMemBuffer(int nInitLen = 16);
- CMemBuffer(const char* pData,int nLen = 0);
- CMemBuffer(const CMemBuffer& buff);
- virtual ~CMemBuffer();
- public:
- virtual int GetBufferLen();
- virtual bool Append(constchar* pData, int nLen = 0);
- virtual char* GetBuffer();
- protected:
- bool ReAllocBuffer(int nDeltaLen);
- protected:
- char* m_pBuffer; // buffer data
- int m_nCurLen;
- int m_nMaxLen;
- };
- #endif // !defined(AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_)
// MemBuffer.h: interface for the CMemBuffer class.
//
//
#if !defined(AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_)
#define AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CMemBuffer
{
public:
CMemBuffer(int nInitLen = 16);
CMemBuffer(const char* pData, int nLen = 0);
CMemBuffer(const CMemBuffer& buff);
virtual ~CMemBuffer();
public:
virtual int GetBufferLen();
virtual bool Append(const char* pData, int nLen = 0);
virtual char* GetBuffer();
protected:
bool ReAllocBuffer(int nDeltaLen);
protected:
char* m_pBuffer; // buffer data
int m_nCurLen;
int m_nMaxLen;
};
#endif // !defined(AFX_MEMBUFFER_H__36D0136D_B57A_49FF_855B_E5545078B130__INCLUDED_)
这个实现没什么好讲的,拿到数据只要Append即可。要获取数据直接使用
GetBuffer()即可。
Lua导出部分
- //
- // @Param
- // zipName which archive
- // fileName which file to extract from the archive named zipName's value
- // password password to extract file from zip
- // @Return
- // binary data
- // error number
- static int luacf_getFileInZip(lua_State* L)
- {
- CMemBuffer buff;
- const char* pZipName = luaL_checkstring(L, 1);
- const char* pFileName = luaL_checkstring(L, 2);
- const char* pPassword = lua_tostring(L, 3);
- int ret = GetFileInZip(buff, pZipName, pFileName, pPassword);
- if (ret >= 0)
- {
- lua_pushlstring(L, buff.GetBuffer(), buff.GetBufferLen());
- return 1;
- }
- lua_pushnil(L);
- lua_pushnumber(L, ret);
- return 2;
- }
- const struct luaL_reg libs[] =
- {
- {"getFileInZip", luacf_getFileInZip},
- {NULL, NULL}
- };
- ZEXTRACT_API int luaopen_zextract(lua_State* L)
- {
- luaL_register(L, "zextract", libs);
- return 1;
- }
//
// @Param
// zipName which archive
// fileName which file to extract from the archive named zipName's value
// password password to extract file from zip
// @Return
// binary data
// error number
static int luacf_getFileInZip(lua_State* L)
{
CMemBuffer buff;
const char* pZipName = luaL_checkstring(L, 1);
const char* pFileName = luaL_checkstring(L, 2);
const char* pPassword = lua_tostring(L, 3);
int ret = GetFileInZip(buff, pZipName, pFileName, pPassword);
if (ret >= 0)
{
lua_pushlstring(L, buff.GetBuffer(), buff.GetBufferLen());
return 1;
}
lua_pushnil(L);
lua_pushnumber(L, ret);
return 2;
}
const struct luaL_reg libs[] =
{
{"getFileInZip", luacf_getFileInZip},
{NULL, NULL}
};
ZEXTRACT_API int luaopen_zextract(lua_State* L)
{
luaL_register(L, "zextract", libs);
return 1;
}
这部分就没啥好说了,总之就是这么简单。
Lua测试代码
- -- 是否是debug版本
- local bDGB = false;
- local zlib;
- if bDGB then
- zlib = package.loadlib("zextract_d.dll", "luaopen_zextract");
- if type(zlib) == "function" then
- zlib = zlib();
- end
- else
- zlib = require "zextract";
- end
- if type(zlib) ~= "table" then
- print("loadlib error");
- return
- end
- print(zlib.getFileInZip("readme.zip", "folder/readme.txt", "123456"))
-- 是否是debug版本
local bDGB = false;
local zlib;
if bDGB then
zlib = package.loadlib("zextract_d.dll", "luaopen_zextract");
if type(zlib) == "function" then
zlib = zlib();
end
else
zlib = require "zextract";
end
if type(zlib) ~= "table" then
print("loadlib error");
return
end
print(zlib.getFileInZip("readme.zip", "folder/readme.txt", "123456"))
这里要讲下对应的载入,前面有个debug版本的判断,这个是根据当前编译的版本决定的,当然也可以debug和release版本都编译,名字是不同的,到时候自己手动修改下这个bDGB的标志即可。二进制的文件就不要用这个代码了,因为最后的数据是print出来的,文本的能看下。载入动态库的部分可以看下之前的文章 《Lua脚本调用C++动态库》
目录结构
一共5个文件夹。
zextract文件夹内为动态库的工程目录。
zlibTest目录为C++调用zextract生成动态库的工程文件。
lib目录为对应的库文件的目录,其中有Zlib的库文件以及zextract动态库生成的库文件。
comm文件为一些公共文件存放区,比如minizip的实现源码,还有内存buffer的实现源码。
bin目录为程序的输出目录,内涵一个lua解析器,lua5.1.dll,zlib1.dll,还有个压缩包的例子readme.zip。还有一个lua文件,测试导出库用的。