Simple ZIP File Routines

Share your advanced PureBasic knowledge/code with the community.
ebs
Enthusiast
Enthusiast
Posts: 469
Joined: Fri Apr 25, 2003 11:08 pm

Simple ZIP File Routines

Post by ebs »

Edit 2019-06-17: Updated code

I don't know if this is already available, but I wrote these simple routines to manipulate ZIP files:
  • ZIP_IsZIP(): check if specified file is a ZIP file
    ZIP_FileCount(): count the number of files in ZIP file
    ZIP_FileList(): get a list of filenames/sizes in ZIP file
    ZIP_ExtractFiles(): extract files in ZIP file to folder
    ZIP_ExtractFile(): extract specified file in ZIP to folder
Feel free to use it if you find it useful!

Code: Select all

UseZipPacker()

#ZIP_USEPATHS = #True
#ZIP_ROOTONLY = #False

Structure _ZIPFile
  Filename.S
  Size.l
EndStructure

Prototype.l ZIP_Callback(Filename.S)

;- append trailing backslash to path (if needed)
Procedure.S TrailingBackslash(Path.S)
  If Path
    If Right(Path, 1) = "\"
      ProcedureReturn Path
    Else
      ProcedureReturn Path + "\"
    EndIf
  EndIf
EndProcedure

;- check if specified file is ZIP
Procedure.l ZIP_IsZIP(ZIPFile.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    IsZIP.l = #True
    ClosePack(hPack)
  EndIf
 
  ProcedureReturn IsZIP
EndProcedure

;- get count of files in specified ZIP file
Procedure.l ZIP_FileCount(ZIPFile.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    If ExaminePack(hPack)
      While NextPackEntry(hPack)
        If PackEntryType(hPack) = #PB_Packer_File
          FileCount.l + 1
        EndIf
      Wend
    EndIf
    ClosePack(hPack)
    ProcedureReturn FileCount
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

;- get list of files/sizes in specified ZIP file
Procedure.l ZIP_FileList(ZIPFile.S, List ZIPFiles._ZIPFile())
  ClearList(ZIPFiles())
 
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    If ExaminePack(hPack)
      While NextPackEntry(hPack)
        If PackEntryType(hPack) = #PB_Packer_File
          AddElement(ZIPFiles())
          With ZIPFiles()
            \Filename = PackEntryName(hPack)
            \Size = PackEntrySize(hPack)
          EndWith
        EndIf
      Wend
    EndIf
    ClosePack(hPack)
    ProcedureReturn ListSize(ZIPFiles())
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

;- extract all files in specified ZIP file to specified folder
;- NB: can now specify 'use paths' or 'root only'
Procedure.l ZIP_ExtractFiles(ZIPFile.S, Folder.S, Paths.l = #ZIP_ROOTONLY, Callback.ZIP_Callback = 0)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    FolderTB.S = TrailingBackslash(Folder)
    If ExaminePack(hPack)
      While NextPackEntry(hPack)
        Select PackEntryType(hPack)
          Case #PB_Packer_File
            Filename.S = PackEntryName(hPack)
            If Callback
              Callback(Filename)
            EndIf
            If UncompressPackFile(hPack, FolderTB + Filename) >= 0
              Count.l + 1
            EndIf
          Case #PB_Packer_Directory
            If Paths = #ZIP_USEPATHS
              ; create subfolder
              CreateDirectory(FolderTB + PackEntryName(hPack))
            EndIf
        EndSelect
      Wend
    EndIf
    ClosePack(hPack)
    ProcedureReturn Count
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

;- extract specified file in specified ZIP file to specified folder
;- NB: this only works with files in the root folder of ZIP file
Procedure.l ZIP_ExtractFile(ZIPFile.S, Folder.S, FileInZIP.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    FolderTB.S = TrailingBackslash(Folder)
    FileSize.l = UncompressPackFile(hPack, FolderTB + FileInZIP, FileInZIP)
    ClosePack(hPack)
    ; return uncompressed size of extracted file, -1 if error
    ProcedureReturn FileSize
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

;- example program
NewList FilesInZIP._ZIPFile()

; file extract callback
Procedure MyZIPCallback(Filename.S)
  Debug Filename
EndProcedure

ZIPFile.S = OpenFileRequester("Select ZIP File", "C:\", "ZIP Files (*.zip)|*.zip|All Files (*.*)|*.*", 0)
If ZIPFile
  ; check if zip file
  If ZIP_IsZIP(ZIPFile)
    ; count files in ZIP
    MessageRequester("ZIP File: " + ZIPFile, Str(ZIP_FileCount(ZIPFile)) + " files")
    ; get list of filenames/sizes in ZIP
    FileCount.l = ZIP_FileList(ZIPFile, FilesInZIP())
    ForEach FilesInZIP()
      With FilesInZIP()
        message.S + \Filename + "  " + Str(\Size) + #LF$
      EndWith
    Next
    If MessageRequester("ZIP File: " + ZIPFile + " - Extract?", Str(FileCount) + " files" + #LF$ + #LF$ + message, #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
      ; extract files to specified folder
      Folder.S = PathRequester("Select Output Folder", "C:\")
      If Folder
        ; extract one file
        ; extract files with/without callback
        ; NB: can now specify 'use paths' or 'root only'
        FileCount.l = ZIP_ExtractFiles(ZIPFile, Folder, #ZIP_USEPATHS, @MyZIPCallback())
        ;FileCount.l = ZIP_ExtractFiles(ZIPFile, Folder)
        MessageRequester("ZIP File: " + ZIPFile, Str(FileCount) + " files extracted")
      EndIf
    EndIf
  Else
    MessageRequester("ZIP File: " + ZIPFile, "File is not a ZIP file!", #MB_ICONERROR)
  EndIf
EndIf
Last edited by ebs on Mon Jun 17, 2019 2:11 pm, edited 1 time in total.
sec
Enthusiast
Enthusiast
Posts: 778
Joined: Sat Aug 09, 2003 3:13 am
Location: 90-61-92 // EU or ASIA
Contact:

Re: Simple ZIP File Routines

Post by sec »

ebs wrote:I don't know if this is already available, but I wrote these simple routines to manipulate ZIP files:
  • ZIP_IsZIP(): check if specified file is a ZIP file
    ZIP_FileCount(): count the number of files in ZIP file
    ZIP_FileList(): get a list of filenames/sizes in ZIP file
    ZIP_ExtractFiles(): extract files in ZIP file to folder
Feel free to use it if you find it useful!

Code: Select all

UseZipPacker()

Structure _ZIPFile
  Filename.S
  Size.l
EndStructure

Prototype.i ZIP_Callback(Filename.S)

;- append trailing backslash to path (if needed)
Procedure.S TrailingBackslash(Path.S)
  If Path
    If Right(Path, 1) = "\"
      ProcedureReturn Path
    Else
      ProcedureReturn Path + "\"
    EndIf
  EndIf
EndProcedure

;- check signature to see if specified file is ZIP
Procedure.l ZIP_IsZIP(ZIPFile.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    IsZIP.l = #True
    ClosePack(hPack)
  EndIf
  
  ProcedureReturn IsZIP
EndProcedure

;- get count of files in specified ZIP file
Procedure.l ZIP_FileCount(ZIPFile.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    While NextPackEntry(hPack)
      If PackEntryType(hPack) = #PB_Packer_File
        FileCount.l + 1
      EndIf
    Wend
    ClosePack(hPack) 
    ProcedureReturn FileCount
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure
;- get list of files/sizes in specified ZIP file
Procedure.l ZIP_FileList(ZIPFile.S, List ZIPFiles._ZIPFile())
  ClearList(ZIPFiles())
  
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    While NextPackEntry(hPack)
      If PackEntryType(hPack) = #PB_Packer_File
        AddElement(ZIPFiles())
        With ZIPFiles()
          \Filename = PackEntryName(hPack)
          \Size = PackEntrySize(hPack)
        EndWith
      EndIf
    Wend
    ClosePack(hPack) 
    ProcedureReturn ListSize(ZIPFiles())
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure
;- extract files in specified ZIP file to specified folder
Procedure.l ZIP_ExtractFiles(ZIPFile.S, Folder.S, Callback.ZIP_Callback = 0)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    FolderTB.S = TrailingBackslash(Folder)
    While NextPackEntry(hPack)
      If PackEntryType(hPack) = #PB_Packer_File
        Filename.S = PackEntryName(hPack)
        If Callback
          Callback(Filename)
        EndIf
        UncompressPackFile(hPack, FolderTB + Filename)
        Count.l + 1
      EndIf
    Wend
    ClosePack(hPack) 
    ProcedureReturn Count
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

NewList FilesInZIP._ZIPFile()

; file extract callback
Procedure MyZIPCallback(Filename.S)
  Debug Filename
EndProcedure

ZIPFile.S = OpenFileRequester("Select ZIP File", "C:\", "ZIP Files (*.zip)|*.zip|All Files (*.*)|*.*", 0)
If ZIPFile
  ; check if zip file
  If ZIP_IsZIP(ZIPFile)
    ; count files in ZIP
    MessageRequester("ZIP File: " + ZIPFile, Str(ZIP_FileCount(ZIPFile)) + " files")
    ; get list of filenames/sizes in ZIP
    FileCount.l = ZIP_FileList(ZIPFile, FilesInZIP())
    ForEach FilesInZIP()
      With FilesInZIP()
        message.S + \Filename + "  " + Str(\Size) + #LF$
      EndWith
    Next
    If MessageRequester("ZIP File: " + ZIPFile + " - Extract?", Str(FileCount) + " files" + #LF$ + #LF$ + message, #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
      ; extract files to specified folder
      Folder.S = PathRequester("Select Output Folder", "C:\")
      If Folder
        ; extract files with/without callback
        FileCount.l = ZIP_ExtractFiles(ZIPFile, Folder, @MyZIPCallback())
        ;FileCount.l = ZIP_ExtractFiles(ZIPFile, Folder)
        MessageRequester("ZIP File: " + ZIPFile, Str(FileCount) + " files extracted") 
      EndIf
    EndIf
  Else
    MessageRequester("ZIP File: " + ZIPFile, "File is not a ZIP file!", #MB_ICONERROR)
  EndIf
EndIf

Great stuff, thx.

Does ZIP_ExtractFiles support e, x same as 7z command line?
e : Extract files from archive (without using directory names)
x : eXtract files with full paths
ebs
Enthusiast
Enthusiast
Posts: 469
Joined: Fri Apr 25, 2003 11:08 pm

Re: Simple ZIP File Routines

Post by ebs »

sec wrote:Does ZIP_ExtractFiles support e, x same as 7z command line?
e : Extract files from archive (without using directory names)
x : eXtract files with full paths
It's pretty simple code, so it doesn't deal with paths in the ZIP file.
It just extracts the files in the "root directory" of the ZIP file.

The "ZIP_FileList()" will include files in a folder, but they aren't extracted.
That seems like a straightforward thing to add; I'll take a look.

In the meantime, here's a procedure to extract a specified file, not all files:

Code: Select all

;- extract specified file in specified ZIP file to specified folder
Procedure.l ZIP_ExtractFile(ZIPFile.S, Folder.S, FileInZIP.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    FolderTB.S = TrailingBackslash(Folder)
    FileSize.l = UncompressPackFile(hPack, FolderTB + FileInZIP, FileInZIP)
    ClosePack(hPack) 
    ; return uncompressed size of extracted file, -1 if error
    ProcedureReturn FileSize
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure
ebs
Enthusiast
Enthusiast
Posts: 469
Joined: Fri Apr 25, 2003 11:08 pm

Re: Simple ZIP File Routines

Post by ebs »

Here's updated code that supports extracting files from paths or from the root only.

While I was revising the code, I noticed that I had forgotten to use the "ExaminePack()" procedure.
The funny thing is that the code works with or without it. I added it in this version to be correct.

Code: Select all

UseZipPacker()

#ZIP_USEPATHS = #True
#ZIP_ROOTONLY = #False

Structure _ZIPFile
  Filename.S
  Size.l
EndStructure

Prototype.l ZIP_Callback(Filename.S)

;- append trailing backslash to path (if needed)
Procedure.S TrailingBackslash(Path.S)
  If Path
    If Right(Path, 1) = "\"
      ProcedureReturn Path
    Else
      ProcedureReturn Path + "\"
    EndIf
  EndIf
EndProcedure

;- check if specified file is ZIP
Procedure.l ZIP_IsZIP(ZIPFile.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    IsZIP.l = #True
    ClosePack(hPack)
  EndIf
  
  ProcedureReturn IsZIP
EndProcedure

;- get count of files in specified ZIP file
Procedure.l ZIP_FileCount(ZIPFile.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    If ExaminePack(hPack)
      While NextPackEntry(hPack)
        If PackEntryType(hPack) = #PB_Packer_File
          FileCount.l + 1
        EndIf
      Wend
    EndIf
    ClosePack(hPack) 
    ProcedureReturn FileCount
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

;- get list of files/sizes in specified ZIP file
Procedure.l ZIP_FileList(ZIPFile.S, List ZIPFiles._ZIPFile())
  ClearList(ZIPFiles())
  
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    If ExaminePack(hPack)
      While NextPackEntry(hPack)
        If PackEntryType(hPack) = #PB_Packer_File
          AddElement(ZIPFiles())
          With ZIPFiles()
            \Filename = PackEntryName(hPack)
            \Size = PackEntrySize(hPack)
          EndWith
        EndIf
      Wend
    EndIf
    ClosePack(hPack) 
    ProcedureReturn ListSize(ZIPFiles())
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

;- extract all files in specified ZIP file to specified folder
;- NB: can now specify 'use paths' or 'root only'
Procedure.l ZIP_ExtractFiles(ZIPFile.S, Folder.S, Paths.l = #ZIP_ROOTONLY, Callback.ZIP_Callback = 0)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    FolderTB.S = TrailingBackslash(Folder)
    If ExaminePack(hPack)
      While NextPackEntry(hPack)
        Select PackEntryType(hPack)
          Case #PB_Packer_File
            Filename.S = PackEntryName(hPack)
            If Callback
              Callback(Filename)
            EndIf
            If UncompressPackFile(hPack, FolderTB + Filename) >= 0
              Count.l + 1
            EndIf
          Case #PB_Packer_Directory
            If Paths = #ZIP_USEPATHS
              ; create subfolder
              CreateDirectory(FolderTB + PackEntryName(hPack))
            EndIf
        EndSelect
      Wend
    EndIf
    ClosePack(hPack) 
    ProcedureReturn Count
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

;- extract specified file in specified ZIP file to specified folder
;- NB: this only works with files in the root folder of ZIP file
Procedure.l ZIP_ExtractFile(ZIPFile.S, Folder.S, FileInZIP.S)
  hPack.l = OpenPack(#PB_Any, ZIPFile, #PB_PackerPlugin_Zip)
  If hPack
    FolderTB.S = TrailingBackslash(Folder)
    FileSize.l = UncompressPackFile(hPack, FolderTB + FileInZIP, FileInZIP)
    ClosePack(hPack) 
    ; return uncompressed size of extracted file, -1 if error
    ProcedureReturn FileSize
  Else
    ; indicate open error
    ProcedureReturn -1
  EndIf
EndProcedure

;- example program
NewList FilesInZIP._ZIPFile()

; file extract callback
Procedure MyZIPCallback(Filename.S)
  Debug Filename
EndProcedure

ZIPFile.S = OpenFileRequester("Select ZIP File", "C:\", "ZIP Files (*.zip)|*.zip|All Files (*.*)|*.*", 0)
If ZIPFile
  ; check if zip file
  If ZIP_IsZIP(ZIPFile)
    ; count files in ZIP
    MessageRequester("ZIP File: " + ZIPFile, Str(ZIP_FileCount(ZIPFile)) + " files")
    ; get list of filenames/sizes in ZIP
    FileCount.l = ZIP_FileList(ZIPFile, FilesInZIP())
    ForEach FilesInZIP()
      With FilesInZIP()
        message.S + \Filename + "  " + Str(\Size) + #LF$
      EndWith
    Next
    If MessageRequester("ZIP File: " + ZIPFile + " - Extract?", Str(FileCount) + " files" + #LF$ + #LF$ + message, #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
      ; extract files to specified folder
      Folder.S = PathRequester("Select Output Folder", "C:\")
      If Folder
        ; extract one file
        ; extract files with/without callback
        ; NB: can now specify 'use paths' or 'root only'
        FileCount.l = ZIP_ExtractFiles(ZIPFile, Folder, #ZIP_USEPATHS, @MyZIPCallback())
        ;FileCount.l = ZIP_ExtractFiles(ZIPFile, Folder)
        MessageRequester("ZIP File: " + ZIPFile, Str(FileCount) + " files extracted") 
      EndIf
    EndIf
  Else
    MessageRequester("ZIP File: " + ZIPFile, "File is not a ZIP file!", #MB_ICONERROR)
  EndIf
EndIf
BarryG
Addict
Addict
Posts: 1506
Joined: Thu Apr 18, 2019 8:17 am

Re: Simple ZIP File Routines

Post by BarryG »

Thanks for the code, ebs. However, it won't unzip password-protected archives. Is that planned?

Also, to save us scrolling through this thread to find the latest version, I suggest you always edit the first post with the latest code. That's the norm for this type of thing. ;)
ebs
Enthusiast
Enthusiast
Posts: 469
Joined: Fri Apr 25, 2003 11:08 pm

Re: Simple ZIP File Routines

Post by ebs »

First post updated with new code.

Regarding ZIP password - I wrote these routines for a work project where I had no need to work with password-protected ZIP files.
Also, as far as I know, PB has no way to deal with ZIP passwords.
BarryG
Addict
Addict
Posts: 1506
Joined: Thu Apr 18, 2019 8:17 am

Re: Simple ZIP File Routines

Post by BarryG »

Thanks for editing the first post to make it easier to get the latest code. :)

I was hoping you'd know a way to decode passworded zip files yourself with some code. I hope Fred can add encryption/decryption one day.
sec
Enthusiast
Enthusiast
Posts: 778
Joined: Sat Aug 09, 2003 3:13 am
Location: 90-61-92 // EU or ASIA
Contact:

Re: Simple ZIP File Routines

Post by sec »

BarryG wrote:Thanks for editing the first post to make it easier to get the latest code. :)

I was hoping you'd know a way to decode passworded zip files yourself with some code. I hope Fred can add encryption/decryption one day.
You can run
7za.exe -p{Password} : set Password
with RunProgram()
BarryG
Addict
Addict
Posts: 1506
Joined: Thu Apr 18, 2019 8:17 am

Re: Simple ZIP File Routines

Post by BarryG »

Good idea, sec!
Post Reply