【Delphi】从内存读取或解压压缩文件(RAR、ZIP、TAR、GZIP等)(一)
==============================
关键词:压缩文件、压缩包、7zip、SevenZip、RAR、内存读取、内存解压、TAR、GZIP、ZIP
==============================
基于安全软件考虑,有时候为了不在本地生成临时文件,应用程序需要直接在内存打开加载各种类型的文件。
目前比较困难的文件类型是EXE、PDF、压缩文件、音频视频文件等。
其中EXE网络上有部分PELoader的实现源码,当对系统兼容性并不是很好,希望有这方面开发的朋友可以提供相关源码,多谢。
本文涉及内容相对较为简单,但却比较实用。
文件压缩/解压实现原理相对比较复杂,本文也不会讨论压缩算法方面的知识(实在是力不能及)。
对于一般应用程序,要实现从内存直接加载/读取/解析/解压压缩包,使用现有的压缩类库是最好的方案。
在比较各种压缩包开发库后,选择出使用7zip(sevenZip)的库读取各种类型的压缩包,网络上已有封装好7z.dll中各个API的delphi版sevenzip.pas。
本文也可以作为基于7ZIP自主开发解压软件的示例。
使用注意:
1)7z.dll:直接安装7-zip后在安装目录下就有。
2)sevenzip.pas:在本文最后附上
以下是对sevenzip.pas二次封装,实现下面功能:
1)实现LoadFromFile、LoadFromStream函数,对于熟悉VCL的这2个方法会比较亲切
2)实现按多层目录(目录树)获取文件列表的函数GetItems(只是简单实现,方便命令行程序开发)
3)实现解压指定文件函数:ExtractItemToStream、ExtractItemToFile
示例程序:
【Delphi】从内存直接读取加载或解压压缩文件(RAR、ZIP、TAR、GZIP)等(二)
sevenzip.pas源码:
【Delphi】从内存直接读取加载或解压压缩文件(RAR、ZIP、TAR、GZIP)等(三)
源码:
unit D7zUtils;
interface
uses
SysUtils, Classes, Sevenzip;
type
TD7zFileType = (dftZip, dftBZ2, dftRar, dftArj, dftZ, dftLzh, dft7z, dftCab, dftNsis, dftLzma,
dftPe, dftElf, dftMacho, dftUdf, dftXar, dftMub, dftHfs, dftDmg, dftCompound,
dftWim, dftIso, dftBkf, dftChm, dftSplit, dftRpm, dftDeb, dftCpio, dftTar,
dftGZip);
TD7zFileTypes = set of TD7zFileType;
TD7zipStrings = TStrings;
TD7zipStringList = class(TStringList)
public
constructor Create();virtual;
end;
TOnPassword = procedure (Sender: TObject; var sPassword: WideString; var bContinue:Boolean) of object;
TOnProgress = procedure (Sender: TObject; bIsTotal: boolean; iValue: Int64; var bContinue:Boolean) of object;
TD7zipFile = class
private
FInArchive: I7zInArchive;
FItems: TD7zipStrings;
FTmpStream: TStream;
private
FCurrentItemPath: WideString;
FOnPassword: TOnPassword;
FOnProgress: TOnProgress;
private
FPasswordCallback: T7zPasswordCallback;
FProgressCallback: T7zProgressCallback;
function DoOnPassword(var sPassword: WideString): HRESULT;
function DoOnProgress(bIsTotal: boolean; iValue: Int64): HRESULT;
public
constructor Create();virtual;
destructor Destroy;override;
function LoadFromFile(AFileName: WideString):Boolean;
function LoadFromStream(AStream: TStream; AFileTypes: TD7zFileTypes=[]):Boolean;overload;
function LoadFromStream(AStream: TStream; AFileType: TD7zFileType):Boolean;overload;
public
function GetItems(sPath: WideString; iFilter: Integer=0): TD7zipStrings;//iFilter: 0-All 1-File 2 Dir ; sPath-暂不支持通配符
public
function ExtractItemToStream(sFileName: WideString; AStream: TStream):Boolean; //解压/获取单个文件
function ExtractItemToFile(sFileName: WideString; sToFile: WideString):Boolean;//解压/获取单个文件
public
property CurrentItemPath: WideString read FCurrentItemPath;
public
//fileSystem functions
function FileExists(sFileName: WideString):Boolean;
function DirectoryExists(sDirName: WideString):Boolean;
function GetCurrentDir(): WideString;
public
property OnPassword: TOnPassword read FOnPassword write FOnPassword;
property OnProgress: TOnProgress read FOnProgress write FOnProgress;
end;
implementation
function FUNC_PasswordCallback(sender: Pointer; var password: UnicodeString): HRESULT; stdcall;
begin
// call a dialog box ...
//password := 'password';
//Result := S_OK;
Result := S_FALSE;
if sender=nil then Exit;
Result := TD7zipFile(sender).DoOnPassword(password);
end;
function FUNC_ProgressCallback(sender: Pointer; total: boolean;
value: Int64): HRESULT; stdcall;
begin
Result := S_FALSE;
if sender=nil then Exit;
Result := TD7zipFile(sender).DoOnProgress(total, value);
end;
{ TD7zipFile }
constructor TD7zipFile.Create;
begin
FItems:=TD7zipStringList.Create;
FPasswordCallback := FUNC_PasswordCallback;
FProgressCallback := FUNC_ProgressCallback;
end;
destructor TD7zipFile.Destroy;
begin
FItems.Free;
FreeAndNil(FTmpStream);
inherited;
end;
function TD7zipFile.DirectoryExists(sDirName: WideString): Boolean;
var
iIndex:Integer;
begin
Result := False;
if FInArchive=nil then Exit;
if sDirName<>'' then
if sDirName[Length(sDirName)]<>'\' then
sDirName := sDirName +'\';
iIndex := Self.FItems.IndexOf(sDirName);
if iIndex=-1 then Exit;
Result := FInArchive.ItemIsFolder[iIndex];
end;
function TD7zipFile.DoOnPassword(var sPassword: WideString): HRESULT;
var
bContinue: Boolean;
begin
bContinue := True;
if Assigned(FOnPassword) then FOnPassword(Self, sPassword, bContinue);
if bContinue then Result := S_OK else Result := S_FALSE;
end;
function TD7zipFile.DoOnProgress(bIsTotal: boolean;
iValue: Int64): HRESULT;
var
bContinue: Boolean;
begin
bContinue := True;
if Assigned(FOnProgress) then FOnProgress(Self, bIsTotal, iValue, bContinue);
if bContinue then Result := S_OK else Result := S_FALSE;
end;
function TD7zipFile.ExtractItemToFile(sFileName: WideString;
sToFile: WideString): Boolean;
var
AStream: TStream;
begin
AStream:=TFileStream.Create(sToFile, fmCreate);
try
Result := ExtractItemToStream(sFileName, AStream);
finally
AStream.Free;
end;
end;
function TD7zipFile.ExtractItemToStream(sFileName: WideString;
AStream: TStream): Boolean;
var
iIndex: Integer;
begin
Result := False;
if FInArchive=nil then Exit;
iIndex := FItems.IndexOf(sFileName);
if iIndex=-1 then Exit;
try
FInArchive.ExtractItem(iIndex, AStream, False);
Result := True;
except
end;
end;
function TD7zipFile.FileExists(sFileName: WideString): Boolean;
var
iIndex:Integer;
begin
Result := False;
if FInArchive=nil then Exit;
if sFileName<>'' then
if sFileName[Length(sFileName)]<>'\' then
sFileName := sFileName +'\';
iIndex := Self.FItems.IndexOf(sFileName);
if iIndex=-1 then Exit;
Result := not FInArchive.ItemIsFolder[iIndex];
end;
function TD7zipFile.GetCurrentDir: WideString;
begin
Result := FCurrentItemPath;
end;
function TD7zipFile.GetItems(sPath: WideString; iFilter: Integer=0): TD7zipStrings;
function IsItemChild(sParent, sChild: WideString):Boolean;
//var
// sTmp: WideString;
begin
if (sParent='') then //获取全部
Result := True
else if (sParent='\\') then //根目录
Result := Pos('\', sChild)=0
else
begin
Result := (Pos(sParent, sChild)=1) and
(Pos('\', Copy(sChild, Length(sParent)+1, Length(sChild)))=0);
end;
end;
var
I: Integer;
sTmp: WideString;
bDir: Boolean;
begin
Result := TD7zipStringList.Create;
if (sPath='.') then //当前目录
begin
sPath := FCurrentItemPath;
end
else if (sPath='..') then //上一层目录
begin
if (FCurrentItemPath='') then FCurrentItemPath := '\\';
if FCurrentItemPath<>'\\' then
begin
sPath := FCurrentItemPath;
if sPath[Length(sPath)]='\' then
sPath := Copy(sPath, 1, Length(sPath)-1);
sPath := ExtractFilePath(sPath);
end
else
sPath := FCurrentItemPath;
if (sPath='') then sPath := '\\';
end;
if sPath<>'' then
begin
if sPath[Length(sPath)]<>'\' then sPath := sPath +'\';
FCurrentItemPath := sPath;
end;
if (sPath<>'') and (sPath<>'\\') then
begin
Result.Add('.');
Result.Add('..');
end;
for I:=0 to FItems.Count-1 do
begin
bDir := False;
sTmp := FItems.Strings[I];
if sTmp<>'' then
if sTmp[Length(sTmp)]='\' then
begin
sTmp := Copy(sTmp, 1, Length(sTmp)-1);
bDir := True;
end;
if not IsItemChild(sPath, sTmp) then Continue;
if (iFilter=0)
or ((iFilter=1) and (not bDir))
or ((iFilter=2) and (bDir)) then
Result.Add(sTmp);
end;
end;
function TD7zipFile.LoadFromFile(AFileName: WideString): Boolean;
function FileExtToFileTypes(sExt: WideString): TD7zFileTypes;
begin
Result := [];
if (sExt='.ZIP') or (sExt='.JAR') or (sExt='.XPI') then
Include(Result, dftZip)
else if (sExt='.BZ2') or (sExt='.BZIP2') or (sExt='.TBZ2') or (sExt='.TBZ') then
Include(Result, dftBZ2)
else if (sExt='.RAR') or (sExt='.R00') then
Include(Result, dftRar)
else if (sExt='.ARJ') then
Include(Result, dftArj)
else if (sExt='.Z') or (sExt='.TAZ') then
Include(Result, dftZ)
else if (sExt='.LZH') or (sExt='.LHA') then
Include(Result, dftLzh)
else if (sExt='.7Z') then
Include(Result, dft7z)
else if (sExt='.CAB') then
Include(Result, dftCab)
else if (sExt='.NSIS') then
Include(Result, dftNsis) //安装包工具
else if (sExt='.LZMA') or (sExt='.LZMA86') then
Include(Result, dftLzma)
else if (sExt='.EXE') then
begin
Include(Result, dftPe);
Include(Result, dftNsis);
end
else if (sExt='.PE') or (sExt='.DLL') or (sExt='.SYS') then
Include(Result, dftPe)
else if (sExt='.ELF') then
Include(Result, dftElf)
else if (sExt='.MACHO') then
Include(Result, dftMacho)
else if (sExt='.UDF') then
Include(Result, dftUdf)
else if (sExt='.XAR') then
Include(Result, dftXar)
else if (sExt='.MUB') then
Include(Result, dftMub)
else if (sExt='.HFS') or (sExt='.CD') then
Include(Result, dftHfs)
else if (sExt='.DMG') then
Include(Result, dftDmg)
else if (sExt='.MSI') or (sExt='.DOC') or (sExt='.XLS') or (sExt='.PPT') then
Include(Result, dftCompound)
else if (sExt='.WIM') or (sExt='.SWM') then
Include(Result, dftWim)
else if (sExt='.ISO') then
begin
Include(Result, dftIso);
Include(Result, dftUdf);
end
else if (sExt='.BKF') then
Include(Result, dftBkf)
else if (sExt='.CHM') or (sExt='.CHI') or (sExt='.CHQ') or (sExt='.CHW')
or (sExt='.HXS') or (sExt='.HXI') or (sExt='.HXR') or (sExt='.HXQ')
or (sExt='.HXW') or (sExt='.LIT') then
Include(Result, dftChm)
else if (sExt='.001') then
Include(Result, dftSplit)
else if (sExt='.RPM') then
Include(Result, dftRpm)
else if (sExt='.DEB') then
Include(Result, dftDeb)
else if (sExt='.CPIO') then
Include(Result, dftCpio)
else if (sExt='.TAR') then
Include(Result, dftTar)
else if (sExt='.GZ') or (sExt='.GZIP') or (sExt='.TGZ') or (sExt='.TPZ') then
Include(Result, dftGZip);
end;
begin
Result := False;
FreeAndNil(FTmpStream);
FTmpStream:=TFileStream.Create(AFileName, fmOpenRead);
try
Result := LoadFromStream(FTmpStream, FileExtToFileTypes(UpperCase(ExtractFileExt(AFileName))));
finally
if not Result then FreeAndNil(FTmpStream);
end;
end;
function TD7zipFile.LoadFromStream(AStream: TStream; AFileTypes: TD7zFileTypes=[]): Boolean;
var
bUnknowType:Boolean;
AFileType: TD7zFileType;
begin
FInArchive := nil;
Result := False;
try
bUnknowType := AFileTypes=[];
AFileType := Low(TD7zFileType);
while AFileType<High(TD7zFileType) do
begin
if (not Result) and (bUnknowType or (AFileType in AFileTypes)) then
Result := LoadFromStream(AStream, AFileType);
if Result then Break;
Inc(AFileType);
end;
(*
if (not Result) and (bUnknowType or (dftZip in AFileTypes)) then
Result := LoadFromStream(AStream, dftZip);
if (not Result) and (bUnknowType or (dftBZ2 in AFileTypes)) then
Result := LoadFromStream(AStream, dftBZ2);
if (not Result) and (bUnknowType or (dftRar in AFileTypes)) then
Result := LoadFromStream(AStream, dftRar);
if (not Result) and (bUnknowType or (dftArj in AFileTypes)) then
Result := LoadFromStream(AStream, dftArj);
if (not Result) and (bUnknowType or (dftZ in AFileTypes)) then
Result := LoadFromStream(AStream, dftZ);
if (not Result) and (bUnknowType or (dftLzh in AFileTypes)) then
Result := LoadFromStream(AStream, dftLzh);
if (not Result) and (bUnknowType or (dft7z in AFileTypes)) then
Result := LoadFromStream(AStream, dft7z);
if (not Result) and (bUnknowType or (dftCab in AFileTypes)) then
Result := LoadFromStream(AStream, dftCab);
if (not Result) and (bUnknowType or (dftNsis in AFileTypes)) then
Result := LoadFromStream(AStream, dftNsis);
if (not Result) and (bUnknowType or (dftLzma in AFileTypes)) then
Result := LoadFromStream(AStream, dftLzma);
if (not Result) and (bUnknowType or (dftPe in AFileTypes)) then
Result := LoadFromStream(AStream, dftPe);
if (not Result) and (bUnknowType or (dftElf in AFileTypes)) then
Result := LoadFromStream(AStream, dftElf);
if (not Result) and (bUnknowType or (dftMacho in AFileTypes)) then
Result := LoadFromStream(AStream, dftMacho);
if (not Result) and (bUnknowType or (dftUdf in AFileTypes)) then
Result := LoadFromStream(AStream, dftUdf);
if (not Result) and (bUnknowType or (dftXar in AFileTypes)) then
Result := LoadFromStream(AStream, dftXar);
if (not Result) and (bUnknowType or (dftMub in AFileTypes)) then
Result := LoadFromStream(AStream, dftMub);
if (not Result) and (bUnknowType or (dftHfs in AFileTypes)) then
Result := LoadFromStream(AStream, dftHfs);
if (not Result) and (bUnknowType or (dftDmg in AFileTypes)) then
Result := LoadFromStream(AStream, dftDmg);
if (not Result) and (bUnknowType or (dftCompound in AFileTypes)) then
Result := LoadFromStream(AStream, dftCompound);
if (not Result) and (bUnknowType or (dftWim in AFileTypes)) then
Result := LoadFromStream(AStream, dftWim);
if (not Result) and (bUnknowType or (dftIso in AFileTypes)) then
Result := LoadFromStream(AStream, dftIso);
if (not Result) and (bUnknowType or (dftBkf in AFileTypes)) then
Result := LoadFromStream(AStream, dftBkf);
if (not Result) and (bUnknowType or (dftChm in AFileTypes)) then
Result := LoadFromStream(AStream, dftChm);
if (not Result) and (bUnknowType or (dftSplit in AFileTypes)) then
Result := LoadFromStream(AStream, dftSplit);
if (not Result) and (bUnknowType or (dftRpm in AFileTypes)) then
Result := LoadFromStream(AStream, dftRpm);
if (not Result) and (bUnknowType or (dftDeb in AFileTypes)) then
Result := LoadFromStream(AStream, dftDeb);
if (not Result) and (bUnknowType or (dftCpio in AFileTypes)) then
Result := LoadFromStream(AStream, dftCpio);
if (not Result) and (bUnknowType or (dftTar in AFileTypes)) then
Result := LoadFromStream(AStream, dftTar);
if (not Result) and (bUnknowType or (dftGZip in AFileTypes)) then
Result := LoadFromStream(AStream, dftGZip);
*)
except
FInArchive := nil;
end;
end;
function TD7zipFile.LoadFromStream(AStream: TStream;
AFileType: TD7zFileType): Boolean;
var
iIndex, I: Integer;
zStream: IInStream;//T7zStream;
sTmp, sTmpDir: WideString;
iPos: Int64;
sTmpDirListAdd, sTmpDirList: TD7zipStrings;
begin
Result := False;
FInArchive := nil;
FCurrentItemPath := '\\';
FItems.Clear;
iPos := AStream.Position;
case AFileType of
dftZip : FInArchive:= CreateInArchive(CLSID_CFormatZip);
dftBZ2 : FInArchive:= CreateInArchive(CLSID_CFormatBZ2);
dftRar : FInArchive:= CreateInArchive(CLSID_CFormatRar);
dftArj : FInArchive:= CreateInArchive(CLSID_CFormatArj);
dftZ : FInArchive:= CreateInArchive(CLSID_CFormatZ);
dftLzh : FInArchive:= CreateInArchive(CLSID_CFormatLzh);
dft7z : FInArchive:= CreateInArchive(CLSID_CFormat7z);
dftCab : FInArchive:= CreateInArchive(CLSID_CFormatCab);
dftNsis : FInArchive:= CreateInArchive(CLSID_CFormatNsis);
dftLzma : FInArchive:= CreateInArchive(CLSID_CFormatLzma);
dftPe : FInArchive:= CreateInArchive(CLSID_CFormatPe);
dftElf : FInArchive:= CreateInArchive(CLSID_CFormatElf);
dftMacho : FInArchive:= CreateInArchive(CLSID_CFormatMacho);
dftUdf : FInArchive:= CreateInArchive(CLSID_CFormatUdf);
dftXar : FInArchive:= CreateInArchive(CLSID_CFormatXar);
dftMub : FInArchive:= CreateInArchive(CLSID_CFormatMub);
dftHfs : FInArchive:= CreateInArchive(CLSID_CFormatHfs);
dftDmg : FInArchive:= CreateInArchive(CLSID_CFormatDmg);
dftCompound : FInArchive:= CreateInArchive(CLSID_CFormatCompound);
dftWim : FInArchive:= CreateInArchive(CLSID_CFormatWim);
dftIso : FInArchive:= CreateInArchive(CLSID_CFormatIso);
dftBkf : FInArchive:= CreateInArchive(CLSID_CFormatBkf);
dftChm : FInArchive:= CreateInArchive(CLSID_CFormatChm);
dftSplit : FInArchive:= CreateInArchive(CLSID_CFormatSplit);
dftRpm : FInArchive:= CreateInArchive(CLSID_CFormatRpm);
dftDeb : FInArchive:= CreateInArchive(CLSID_CFormatDeb);
dftCpio : FInArchive:= CreateInArchive(CLSID_CFormatCpio);
dftTar : FInArchive:= CreateInArchive(CLSID_CFormatTar);
dftGZip : FInArchive:= CreateInArchive(CLSID_CFormatGZip);
else
Exit;
end;
zStream := T7zStream.Create(AStream);
try
FInArchive.OpenStream(zStream);
FInArchive.SetPasswordCallback(Self, FPasswordCallback);
FInArchive.SetProgressCallback(Self, FProgressCallback);
sTmpDirListAdd := TD7zipStringList.Create;
sTmpDirList := TD7zipStringList.Create;
try
for I:=0 to FInArchive.NumberOfItems-1 do
begin
sTmp := FInArchive.ItemPath[I];
if FInArchive.ItemIsFolder[I] then
begin
if (sTmp<>'') and (sTmp[Length(sTmp)]<>'\') then
sTmp := sTmp+'\';
if sTmpDirList.IndexOf(sTmp)=-1 then
sTmpDirList.Add(sTmp);
//else //已添加到临时列表则需要删除
begin
iIndex := sTmpDirListAdd.IndexOf(sTmp);
if iIndex>-1 then
sTmpDirListAdd.Delete(iIndex);
end;
end
else
begin
sTmpDir := ExtractFilePath(sTmp);
if sTmpDirList.IndexOf(sTmpDir)=-1 then
begin //未添加的文件夹需要添加到临时列表里
if sTmpDirListAdd.IndexOf(sTmpDir)=-1 then
sTmpDirListAdd.Add(sTmpDir);
end;
end;
FItems.Add(sTmp);
end;
FItems.AddStrings(sTmpDirListAdd);
finally
sTmpDirList.Free;
sTmpDirListAdd.Free;
zStream := nil;
end;
Result := True;
except
zStream := nil;
FInArchive := nil;
AStream.Position := iPos;
end;
end;
{ TD7zipStringList }
constructor TD7zipStringList.Create;
begin
Self.CaseSensitive := False;//忽略大小写
end;
end.
浙公网安备 33010602011771号