Search code examples
c#asp.net-mvcarchive7zip

How do I retrieve only one specific file from a 7z instead of unzipping and dumping to a folder?


Currently, I am using 7ZipCLI to unzip my .7z folder to a specified folder destPath like this:

private void ExtractFile(string archivePath, string destPath)
{
    string zpath = @"C:\Program Files\7-Zip\x64\7za.exe";
    try
    {
        ProcessStartInfo pro = new ProcessStartInfo();
        pro.WindowStyle = ProcessWindowStyle.Hidden;
        pro.FileName = zpath;
        pro.Arguments = string.Format("x \"{0}\" -y -o\"{1}\"", archivePath, destPath);
        Process x = Process.Start(pro);
        x.WaitForExit();
    }
    catch (System.Exception Ex)
    {
        Console.WriteLine("{0} Exception: ", Ex)
    }
} 

This takes a long time because the application unzips the folder, unloads it into destPath then does a search within destPath for the specified file. How would I instead look into the .7z, find the specified file and copy just that file to destPath?


Solution

  • If you want to do interesting things with archives I would suggest that you use a library instead of rolling your own process execution solution. There are NuGet packages for just about every task, and this is no exception.

    For instance, SharpCompress is a fairly common library that handles decompressing 7z archives well enough for most uses. Add that to your project and try something like this:

    // Usings:
    //  SharpCompress.Archives;
    //  SharpCompress.Common;
    //  System.Linq;
    
    private static bool ExtractFile(string archivePath, string destPath, string fileSubstring)
    {
        using (var archive = ArchiveFactory.Open(archivePath))
        {
            var entry = archive.Entries.FirstOrDefault(e => e.Key.Contains(fileSubstring));
            if (entry != null)
            {
                var opt = new ExtractionOptions
                {
                    ExtractFullPath = false,
                    Overwrite = true
                };
                try
                {
                    entry.WriteToDirectory(destPath, opt);
                    return true;
                }
                catch { }
            }
        }
        return false;
    }
    

    That's a simple example. You could pass in a filter predicate and process multiple results, whatever fits your requirements.

    Running a few tests here using SysInternals ProcMon to confirm, this does not create extraneous files and works quickly pulling little files out of big archives.

    And as a bonus it doesn't care what type of archive you give it, as long as it's one that's supported by the library. It'll read RAR, ZIP, 7z and a host of others, and you can use the same library to do compression for a few common formats if needed.