Reputation: 45
I have this code that let's the user select multiple zip files and it will copy all files containing the word "unformatted" in it's name and put it in a folder selected by the user. I don't understand why it doesn't copy to the folder.
thank you for your help
Option Explicit
Sub ExtractUnformattedFilesFromZips()
'Ask user to select one or more zip files to extract from
Dim ZipFiles As Variant
ZipFiles = Application.GetOpenFilename(FileFilter:="Zip Files (*.zip), *.zip", Title:="Select one or more zip files to extract from", MultiSelect:=True)
'Ask user to select output folder where "Unformatted" folder will be created
Dim OutputFolder As String
With Application.fileDialog(msoFileDialogFolderPicker)
.Title = "Select output folder where Unformatted folder will be created"
.Show
If .SelectedItems.Count = 1 Then
OutputFolder = .SelectedItems(1)
Else
Exit Sub 'User cancelled or selected more than one folder
End If
End With
'Create Unformatted folder in the output folder
On Error Resume Next 'Avoid error if Unformatted folder already exists
MkDir OutputFolder & "\Unformatted"
On Error GoTo 0
'Loop through each selected zip file and extract files with "unformatted" in the name to the Unformatted folder
Dim ZipFilePath As Variant
Dim UnformattedFolderPath As String
UnformattedFolderPath = OutputFolder & "\Unformatted\"
Dim FileInZip As Variant
Dim ExtractPath As String
For Each ZipFilePath In ZipFiles
If ZipFilePath <> False Then 'User didn't cancel selection
ExtractPath = OutputFolder & "\" & Left$(ZipFilePath, Len(ZipFilePath) - 4) & "\" 'Create subfolder with the same name as the zip file
On Error Resume Next 'Avoid error if subfolder already exists
MkDir ExtractPath
On Error GoTo 0
Debug.Print "Extracting from " & ZipFilePath & " to " & ExtractPath
With CreateObject("Shell.Application").Namespace(ZipFilePath)
For Each FileInZip In .Items
If InStr(1, FileInZip.Name, "unformatted", vbTextCompare) > 0 Then 'File name contains "unformatted"
.CopyHere FileInZip, 16 'Extract file to output folder without prompt and overwrite existing file
Debug.Print "Extracting " & FileInZip.Name & " from " & ZipFilePath & " to " & UnformattedFolderPath
.CopyHere FileInZip, 256 'Extract file to Unformatted folder without prompt and overwrite existing file
Debug.Print "Extracting " & FileInZip.Name & " from " & ZipFilePath & " to " & ExtractPath
End If
Next
End With
End If
Next
'Display message box indicating completion
MsgBox "Extraction complete.", vbInformation
End Sub
Upvotes: 1
Views: 1682
Reputation: 1766
I built another general purpose function for extracting files from ZIP files.
You can extract all the ZIP contente or specific files or files within specific paths.
'Extracts files from a zip file to a folder, with possibility to specify which files to extract
' ARGUMENTS:
' * ZipFilePath = the path to the ZIP file
'
' * OutputFolder = the path where to extract files
'
' * SpecificFiles (OPTIONAL) = list of specific files to extract. If omitted, all files will be extracted.
' Can contain folder paths to specify an exact file inside the zip.
' Does not work with wildcards.
' Examples:
' filetest.txt -> this will extract any file with this name in any subfolder
' \filetest.txt -> this will only extract filetest.txt if it is in the root folder
' \test\filetest.txt -> this will only extract filetest.txt if it is in the 'test' folder
'
' * Options (FLAGS) = Options for extraction. To add options, use + operator (ex. 4+1024)
' Some of these options only work when SpecificFiles is omitted
' (4) Do not display a progress dialog box.
' (8) Give the file being operated on a new name in a move, copy, or rename operation if
' a file with the target name already exists.
' (16) Respond with "Yes" / "Yes to All" for any dialog box that is displayed.
' (64) Preserve undo information, if possible.
' (128) Perform the operation on files only if a wildcard file name (*.*) is specified.
' (256) Display a progress dialog box but do not show the file names.
' (512) Do not confirm the creation of a new directory if the operation requires one to be created.
' (1024) Do not display a user interface if an error occurs.
' (2048) Version 4.71. Do not copy the security attributes of the file.
' (4096) Only operate in the local directory. Do not operate recursively into subdirectories.
' (8192) Version 5.0. Do not copy connected files as a group. Only copy the specified files.
'
' do_not_use = this variable is used for recursion, to pass the current folder
Function Extract_from_zip_file(ZipFilePath As Variant, OutputFolder As Variant, Optional ByRef SpecificFiles As collection = Nothing, Optional Options As Variant = 0, Optional do_not_use As String = "") As Boolean
Dim res As Boolean
'These objects require adding a reference to "Microsoft Shell Controls And Automation" in Tools / References menu
'Dim sh As New shell
'Dim item As Shell32.FolderItem
'Dim ZipFolder As Shell32.Folder
'As an alternative, without adding reference (but without intellisense)
'This also requires ZipFilePath and OutputFolder to be declared as Variant instead of String
Dim sh
Dim item
Dim ZipFolder
Set sh = CreateObject("Shell.Application")
'Namespace returns a Shell32.Folder object
' Reference: https://learn.microsoft.com/en-us/windows/win32/shell/folder
Set ZipFolder = sh.Namespace(ZipFilePath)
If ZipFolder Is Nothing Then
'Zip file does not exist or is invalid
Exit Function
End If
If SpecificFiles Is Nothing Then
'No specific file was requested, so we can extract all, using the easy way
MkDir_recursive OutputFolder
'Reference: https://learn.microsoft.com/en-us/windows/win32/shell/folder-copyhere
sh.Namespace(OutputFolder).CopyHere ZipFolder.items, Options
Extract_from_zip_file = True
Exit Function
Else
'Specific files were requested
'FolderItems is a Shell32.FolderItems object
' Reference: https://learn.microsoft.com/en-us/windows/win32/shell/folderitems
For Each item In ZipFolder.items
'Item is a Shell32.FolderItem object
' Reference: https://learn.microsoft.com/en-us/windows/win32/shell/folderitem
If item.IsFolder And (Options And 4096) = 0 Then
'Is a folder. Do a recursive extraction, except if option 4096 is set
'file = OutputFolder & IIf(Right(OutputFolder, 1) = "\", "", "\") & item.Name
res = Extract_from_zip_file(item.path, OutputFolder & IIf(Right(OutputFolder, 1) = "\", "", "\") & item.Name, SpecificFiles, Options, do_not_use & "\" & item.Name)
If res = False Then Exit Function
Else
'Is a file, check if is listed for extraction
'The list allows for relative paths inside the zip file. Ex: folder1\file.exe
If Collection_Contains_String(SpecificFiles, item.Name) Or _
Collection_Contains_String(SpecificFiles, do_not_use & "\" & item.Name) Or _
Collection_Contains_String(SpecificFiles, Replace(do_not_use, "\", "", 1, 1) & "\" & item.Name) Then
MkDir_recursive OutputFolder
sh.Namespace(OutputFolder).CopyHere item, Options
End If
End If
Next
End If
Extract_from_zip_file = True
End Function
'Checks if a Collecion contains a string
Function Collection_Contains_String(ByRef collection As collection, value As String, Optional Case_sensitive As Boolean = False) As Boolean
Dim i As Long
For i = 1 To collection.count
If VarType(collection(i)) = 8 Then
If (Case_sensitive And (collection(i) = value)) Or _
(Not Case_sensitive And (LCase(collection(i)) = LCase(value))) Then
Collection_Contains_String = True
Exit Function
End If
End If
Next
End Function
And an example of usage:
'Extract some specific files from
Dim coll As New collection
coll.Add "any_file_with_this_name.exe"
coll.Add "\specific_file_in_root_folder.exe"
coll.Add "\TEST\specific_file_in_TEST_folder.exe"
Call Extract_from_zip_file("c:\MyZipFile.zip", "c:\ExtractTo", coll)
'Extract all files
Call Extract_from_zip_file("c:\MyZipFile.zip", "c:\ExtractTo")
Upvotes: 0
Reputation: 166755
This worked for me:
Sub ExtractUnformattedFilesFromZips()
Dim ZipFiles As Variant, ZipFilePath As Variant, UnformattedFolderPath As Variant
Dim FileInZip As Variant, ExtractPath As Variant, OutputFolder As Variant
Dim haveDir As Boolean, oApp As Object
ZipFiles = Application.GetOpenFilename(FileFilter:="Zip Files (*.zip), *.zip", _
Title:="Select one or more zip files to extract from", MultiSelect:=True)
If Not IsArray(ZipFiles) Then Exit Sub
OutputFolder = UserSelectFolder( _
"Select output folder where Unformatted folder will be created")
If Len(OutputFolder) = 0 Then Exit Sub
UnformattedFolderPath = OutputFolder & "\Unformatted\"
EnsureDir UnformattedFolderPath
Set oApp = CreateObject("Shell.Application")
For Each ZipFilePath In ZipFiles
haveDir = False 'reset flag
Debug.Print "Extracting from " & ZipFilePath & " to " & ExtractPath
With oApp.Namespace(ZipFilePath)
For Each FileInZip In .Items
If InStr(1, FileInZip.Name, "unformatted", vbTextCompare) > 0 Then 'File name contains "unformatted"
If Not haveDir Then 'already have an output folder for this zip?
ExtractPath = UnformattedFolderPath & BaseName(ZipFilePath)
EnsureDir ExtractPath
haveDir = True
End If
Debug.Print , FileInZip
oApp.Namespace(ExtractPath).CopyHere FileInZip, 256
End If
Next
End With
Next
MsgBox "Extraction complete.", vbInformation
End Sub
'Ask user to select a folder
Function UserSelectFolder(sPrompt As String) As String
With Application.FileDialog(msoFileDialogFolderPicker)
.AllowMultiSelect = False
.Title = sPrompt
If .Show = -1 Then UserSelectFolder = .SelectedItems(1)
End With
End Function
'Make sure a folder exists
Sub EnsureDir(dirPath)
If Len(Dir(dirPath, vbDirectory)) = 0 Then
MkDir dirPath
End If
End Sub
'get a filename without extension
Function BaseName(sName)
BaseName = CreateObject("scripting.filesystemobject").getbasename(sName)
End Function
Upvotes: 1