Introduction
This week I want to discuss the trouble you may have when searching hundreds of zip files for specific files inside them.
Recognizable?
At least I faced this problem some time ago, and solved it using SSIS and the commandline version of 7-zip, named 7za.exe.
The choice for SSIS was purely based on convenience, as you could tackle this problem also using Powershell, C# or almost any other scripting language.
But I thought SSIS is just a neat and quick way to do this.
Problem
Finding specific files in huge amounts of zip files.
Solution
To tackle this problem I have made a fully configurable SSIS package, that can be executed with different parameters.
The main task of the package is to extract specific files (based on a file name pattern) from zipfiles in a folder and – optionally – subfolders.
The unzipping itself is done with 7za.exe, which is the commandline version of 7z, the great open source zip program, that exists already for several years and is used by millions of users.
By the way, the package is made using SSIS 2014.
Preparations: download and install 7za
It is important to download this package from the official 7z web site, just to avoid that you would get a copy from some deceitful web site infected with malware.
To go to the official 7-zip download page click on this link:
7-zip download page
Most important is the 7-Zip Extra: standalone console version.
For the example I have used version 16.02 (2016-05-21) for Windows, but it should also work with version 9.20 (2010-11-18), then called 7-Zip Command Line Version.
If you download the newest version, you also need software to unzip the 7z160-extra.7z file, for instance the regular 32-bit or 64-bit version of 7-zip, which can also be downloaded on the same page.
Download page of 7-zip
I assume you will use the newest version 7z160-extra.7z
Once unzipped, just take the time to read License.txt, readme.txt and history.txt.
Then copy the three files 7zxa.dll, 7za.dll and 7za.exe to a folder on the Windows computer where SSIS runs, usually a Windows Server.
If you run a x64 version of Windows, you can take the x64 version of the 7za files, which are in a subfolder “x64” of the unzipped files.
Unzipped 7za files ..
I have created a subfolder “7za” in the “C:\Program files” folder of my virtual Windows Server, and copied the x64-version of the three mentioned files to it, but you can put them in any folder you like.
When you have done this, the real fun can start ..
Overview
Here is a picture of the end result:
The Control Flow
Detailed steps
Parameters and variables
First you need to add a few parameters and variables to the package.
Parameters:
Name | Data type | Required | Description |
---|---|---|---|
_7zaPathAndFilename | String | False | The full path and filename of 7za.exe. |
FilenamePattern | String | True | The filename pattern of files you want to unzip. |
SourceRootFolder | String | True | The main folder where you want to search for zip files. |
TraverseSubfolders | Boolean | False | Indication if you want to look only in SourceRootFolder, or also in its subfolders. |
UnzipRootFolder | String | True | The folder to which files are unzipped. Important: this may NOT be a subfolder of SourceRootFolder. |
ZipFilenamePattern | String | True | The filename pattern of the zipfiles from which you want to unzip files. |
Package parameters
Variables:
Name | Data type | Description |
---|---|---|
FLC_UnzipFolder | String | The subfolder where the files will be unzipped. This is a subfolder of the UnzipRootFolder, with the same relative subpath as the zipfile has in SourceRootFolder. Additionally, the zipfilename has been replaced with a subfolder that is the same as the zipfilename without the file extension. |
FLC_ZipPathAndFileName | String | The zipfile full path, that is target of investigation if any files with FilenamePattern are zipped inside. |
SSIS variables needed to do the job
Developing the package
FELC Unzip files
This foreach loop container finds all zip files.
In the Foreach Loop Editor, select Collection in the left pane.
As we want to make the solution generic by using all those parameters mentioned above, we have to add a few expressions.
Foreach Loop container Collection configuration
Add the following three expressions (in my opinion the editor is not very userfriendly, the window is too small, but it can be done):
Property | Expression |
---|---|
Directory | @[$Package::SourceRootFolder] |
FileSpec | @[$Package::ZipFilenamePattern] |
Recurse | @[$Package::TraverseSubfolders] |
Please note that these expressions will set the values for Folder, Files and the Traverse subfolders checkbox in the Enumerator configuration part of the screen (inside the purple square in the picture above).
If you close and reopen the Foreach Loop Editor, you will notice that the configuration fields now use the values from the corresponding parameters, except the value Fully qualified for Retrieve file name, which is hardcoded.
Example of how to set an expression..
Then, still in the Foreach Loop Editor, select Variable Mappings in the left pane and map variable User::FLC_ZipPathAndFileName to Index 0, as shown in the picture below.
Variable Mappings
EXPR Set Unzip folder
The next nifty thing that needs to be done, is creating a folder to store the unzipped files. As we do not know if zipped files with the same name exist in multiple zip files, the safest thing to do is to create a directory structure identical to the directory structure of the zip files.
Additionally, the basename of the zip file (so without file extension) is used as an extra subfolder. In this way we are sure that unzipped files with the same name can be stored without overwriting other ones.
To do this, from the SSIS Toolbox drag an Expression Task inside the Foreach Loop container.
Open the Expression Task and paste this expression into the Expression textbox.
REPLACE(LEFT(@[User::FLC_ZipPathAndFileName],
FINDSTRING(@[User::FLC_ZipPathAndFileName], "."
+ TOKEN(@[User::FLC_ZipPathAndFileName], ".",
TOKENCOUNT(@[User::FLC_ZipPathAndFileName], ".")),1) -1),
@[$Package::SourceRootFolder], @[$Package::UnzipRootFolder])
Then press the Evaluate Expression button to check that the expression is valid.
Configuring the Expression Task
FSYS Create Unzip folder
In the previous step we have composed the full path to unzip files into, but this folder structure might not exist yet.
Therefore we use A File System Task named FSYS Create Unzip folder to create the folder structure.
The good news is that the File System Task can create multiple (sub)folders at once.
Configure it as shown in the picture.
Configuration of FSYS Create Unzip folder
EPR Unzip Selected Files
Below you see a few screenshots for the configuration of the Execute Process Task named EPR Unzip Selected Files.
The funny thing is, that in the Process pane, you do not have to change anything. Executable and Arguments are set using expressions (coming next) and all the other properties can keep their default values.
Process configuration.
Setting Executable and Arguments using expressions
Arguments (copy-friendly):
SCR Remove Empty folders
There is one problem with the steps done so far. If a zipfile does not contain a file that matches the FilenamePattern, then the folderstructure is made for nothing.
However you could just leave these empty folders, I think the solution is a bit better if these empty folders are deleted.
To do this, from the SSIS Toolbox drag an Script Task inside the Foreach Loop container.
In the Script Task Editor, add both variables User::FLC_UnzipFolder and $Package::UnzipRootFolder to ReadOnlyVariables
In the Script Task Editor window, press Edit Script ….
Please note that you can download the C# script here..
Then in the namespaces region add two namespaces, as indicated below.
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
// Added namespaces:
using System.IO;
using System.Linq;
#endregion
Adjust public void main() as follows:
public void Main()
{
// Copy SSIS variable value of FLC_UnzipFolder to C# string variable.
string unzipFolder = Dts.Variables["User::FLC_UnzipFolder"].Value.ToString();
// Source: http://stackoverflow.com/questions/22520761/delete-all-empty-directories-in-specified-path
// My credits go to author "Jon" from Berlin.
// Because not all zipfiles might contain files with the FilenamePattern we are looking for, an empty path might have been created.
// This script deletes these empty folders, so that in the end only folders will exist with unzipped files or subfolders in it.
var di = new DirectoryInfo(unzipFolder);
var root = Dts.Variables["$Package::UnzipRootFolder"].Value.ToString(); // no trailing slash!
while (di.FullName != root
&& !di.EnumerateFiles().Any()
&& !di.EnumerateDirectories().Any())
{
di.Delete();
di = di.Parent;
}
Dts.TaskResult = (int)ScriptResults.Success;
}
Download the script here.
The proof of the pudding is in the eating
Time to do some testing.
I have created a few folders with subfolders and subsubfolders. You can see the folder structure in the last picture below.
As you might have noticed the default values for my parameters already prepared for the testcase: I want to unzip only customer files from zip files with 201605 in the name.
Executing the package in Debug mode..
Below you can see the result. Only customer files are unzipped. The subfolders under the Archive folder that contain no customer zip files (but only currency files), do not exist under the Unzipped folder.
Folder structure of Archive and Unzipped rootfolders..
Unzipped “Customer” files ..
Important notes for using with huge amounts of zip files
From my experience I must also warn you. If you run this package on a folder containing gigabytes of zipfiles, there are two things to be aware of:
- The execution of the package can potentially take a long time, several hours is not unusual.
- Make sure you have enough disk space. Suppose you have the following hypothetical scenario: zip files: 100GB, average zip compression rate: 20%, percentage of files to be unzipped: 30%. In this case you are going to unzip files that have a zipped size of 30% of 100GB, is 30GB. Once unzipped, their size will be 30GB * (1/0,20) = 150GB. You need to have that disk space available on the disk where the Unzipped folder resides.
Conclusion / Wrap up
With this SSIS package you can selectively unzip files from archived zip files.
When using it, make sure you have enough disk space, and be aware that the package can run for several hours, depending on the number and (average) size of the zip files to process.
(c) 2016 hansmichiels.com – Do not steal the contents – spread the link instead – thank you.
One thought on “Unzip specific files from lots of zipped archives (SSIS Series)”