zoukankan      html  css  js  c++  java
  • 【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.
  • 相关阅读:
    unittest用法和report输出
    python断言方式
    python闭包等
    html 基础
    Python装饰器
    python递归取出n层嵌套列表里的所有元素
    pycharm问题集锦
    Coding the Futurn
    python3-端口扫描(TCP_ACK扫描,NULL扫描,windows扫描,xmas扫描)
    python3-端口扫描(TCP connect扫描,SYN扫描,FIN扫描)
  • 原文地址:https://www.cnblogs.com/caibirdy1985/p/4232949.html
Copyright © 2011-2022 走看看