phpwhatthertf
phpwhatthertf

Reputation: 83

C# to unzip all nested zips in a directory while preserving file structure

I am trying to unzip all zips within a directory, which sometimes contain nested zips multiple layers down. I want to also preserve the original zip nesting structure in the extracted version. I am able to do what I want with one layer of zips, but not for nested zips or zips inside subfolders.

For example I want to turn everything in the starting folder like this:

[Starting Folder]
-Zip1.zip
--NestedZip1.zip
---text1.txt
--NestedZip2.zip
---text2.txt
-Zip2.zip
--[Subfolder1]
---Zip3.zip
----text3.txt
---Zip4.zip
----text4.txt

And extract everything to a folder in that same starting directory:

[Starting Folder]
[Extracted Folder]
-Zip1 (folder)
--NestedZip1 (folder)
---text1.txt
--NestedZip2 (folder)
---text2.txt
-Zip2 (folder)
--[Subfolder1]
---Zip3 (folder)
----text3.txt
---Zip4 (folder)
----text4.txt

Right now I am using this to unzip all the files in MyGlobals.finalPathForWork (which is the starting directory) and it works but only unzips one layer of zips. I need it to run again somehow in case there were zips in that first layer of zips.

        public static void MyMethod3()
    {
        string startPath = MyGlobals.finalPathForWork;
        string extractPath = MyGlobals.finalPathForWork + @"\\Extracted\";
        Directory.GetFiles(startPath, "*.zip", SearchOption.AllDirectories).ToList()
            .ForEach(zipFilePath =>
            {
                var extractPathForCurrentZip = Path.Combine(extractPath, Path.GetFileNameWithoutExtension(zipFilePath));
                if (!Directory.Exists(extractPathForCurrentZip))
                {
                    Directory.CreateDirectory(extractPathForCurrentZip);
                }
                ZipFile.ExtractToDirectory(zipFilePath, extractPathForCurrentZip);
            });
    }

I've tried applying/combining pieces of this: How to unzip multi layered zip files in C#

public static void ExtractFile(string baseZipPath, string extractPath)
{
        if (!Directory.Exists(extractPath))
            Directory.CreateDirectory(extractPath);

        ZipFile.ExtractToDirectory(baseZipPath, extractPath);
        string[] nestedZipDirectories = System.IO.Directory.GetFiles(extractPath, "*.zip");
        foreach (var nestedZipDirectory in nestedZipDirectories)
        {
            ZipFile.ExtractToDirectory(nestedZipDirectory, extractPath);
        }
}

static void Main(string[] args)
{
    ExtractFile(@"c:\myfolder\grandfather.zip", @"c:\myfolder2");
}

Is there another way to loop the searching/unzipping process down through all subfolders and nested zip files? Or should that other solution above work and I'm must just be incorporating it incorrectly?

Upvotes: 1

Views: 2309

Answers (2)

tegbir
tegbir

Reputation: 41

    //Here is the complete working & tested code. Before running, just change the zipPath and extractPath.

    static void Main(string[] args)
    {
        string zipPath = @"C:\TestZipFiles\test.zip"; 
        string extractPath = @"C:\TestZipFiles\extractfiles";
        int dbFileCount = 0;
        ExtractRootFile(zipPath, extractPath, ref dbFileCount);
    }

    private static void DeleteSubFolderAndFiles(string destinationPath)
    {
        string[] fileEntries = Directory.GetFiles(destinationPath);
        foreach (string fileName in fileEntries)
            File.Delete(fileName);

        string[] subdirectoryEntries = 
        Directory.GetDirectories(destinationPath);
        foreach (string subdirectory in subdirectoryEntries)
        {
            DeleteSubFolderAndFiles(subdirectory);
            if (Directory.GetFiles(subdirectory).Length == 0 && 
         Directory.GetDirectories(subdirectory).Length == 0)
            {
                Directory.Delete(subdirectory);
            }
        }
    }
    public static void ExtractRootFile(string baseZipPath, string extractPath, ref int dbFileCount)
    {
        if (Directory.Exists(extractPath))
            DeleteSubFolderAndFiles(extractPath);

        if (!Directory.Exists(extractPath))
            Directory.CreateDirectory(extractPath);

        ZipFile.ExtractToDirectory(baseZipPath, extractPath);
        extractPath = extractPath + @"\test"; //replace this with dynamic value of first zip file only.
        RecursivelyExtractChildFolders(extractPath, ref dbFileCount);
    }
    public static void RecursivelyExtractChildFolders(string extractPath, ref int dbFileCount)
    {
        string[] nestedZipDirectories = System.IO.Directory.GetFiles(extractPath, "*.zip");
        string[] nestedDBDirectories = System.IO.Directory.GetFiles(extractPath, "*.db");
        if (nestedDBDirectories.Length > 0)
            dbFileCount = dbFileCount + nestedDBDirectories.Length;

        foreach (var nestedZipDirectory in nestedZipDirectories)
        {
            ZipFile.ExtractToDirectory(nestedZipDirectory, extractPath);
        }

        if (Directory.Exists(extractPath))
        {
            string[] subdirectoryEntries = 
            Directory.GetDirectories(extractPath);
            foreach (string subdirectory in subdirectoryEntries)
            {
                RecursivelyExtractChildFolders(subdirectory, ref dbFileCount);
            }
        }
    }

Upvotes: 0

Zohar Peled
Zohar Peled

Reputation: 82514

This is a classic use-case for recursion.
Note the comments in the code:

public static void ExtractFile(string zipFilePath, string extractPath)
{
    // If directory already exist, CreateDirectory does nothing
    Directory.CreateDirectory(extractPath); 

    // Extract current zip file
    ZipFile.ExtractToDirectory(zipFilePath, extractPath);

    // Enumerate nested zip files
    var nestedZipFiles = Directory.EnumerateFiles(extractPath, "*.zip");

    // iterate the enumerator
    foreach (var nestedZipFile in nestedZipFiles)
    {    
        // Get the nested zip full path + it's file name without the ".zip" extension
        // I.E this "C:\users\YourUserName\Documents\SomeZipFile.Zip" - turns to this: "C:\users\YourUserName\Documents\SomeZipFile".
        var nestedZipExtractPath = Path.Combine(Path.GetDirectoryName(nestedZipFile), Path.GetFileNameWithoutExtension(nestedZipFile));

        // extract recursively
        ExtractFile(nestedZipFile, nestedZipExtractPath);
    }
}

Upvotes: 5

Related Questions