Search This Blog

Monday, April 30, 2018

Wrapper for SharpZipLib (C# compressing nuget package)

Wrapper I wrote for SharpZipLib:


using System;
using System.IO;
using ICSharpCode.SharpZipLib.Zip;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.SharpZipLib.Core;

public static class SharpZipLibHelper
{
    //I wrapped the author's samples at https://github.com/icsharpcode/SharpZipLib/wiki/Zip-Samples

    #region Functions

    private static bool _ignore(List<string> excludes, string fileName)
    {
        foreach (var exclude in excludes)
        {
            //ignore a group of files with a given extension if it was specified in the excludes list
            if (exclude.IndexOf("*") > -1)
            {
                var fileExtension = Path.GetExtension(fileName);
                var excludeExtension = Path.GetExtension(exclude);
                if (string.Equals(excludeExtension, fileExtension, StringComparison.InvariantCultureIgnoreCase))
                    return true;
            }
            //ignore a specific file if it was specified in the excludes list
            else
            {
                if (string.Equals(fileName, exclude, StringComparison.InvariantCultureIgnoreCase))
                    return true;
            }
        }

        return false;
    }

    private static void _compressFolder(string path, ZipOutputStream zipStream, int folderOffset, List<string> excludes = null)
    {
        var files = Directory.GetFiles(path);

        foreach (string filename in files)
        {
            FileInfo fi = new FileInfo(filename);

            if (excludes == null) excludes = new List<string>();
            if (_ignore(excludes, fi.Name)) continue;

            string entryName = filename.Substring(folderOffset); // Makes the name in zip based on the folder
            entryName = ZipEntry.CleanName(entryName); // Removes drive from name and fixes slash direction
            ZipEntry newEntry = new ZipEntry(entryName);
            newEntry.DateTime = fi.LastWriteTime; // Note the zip format stores 2 second granularity

            // Specifying the AESKeySize triggers AES encryption. Allowable values are 0 (off), 128 or 256.
            // A password on the ZipOutputStream is required if using AES.
            //   newEntry.AESKeySize = 256;

            // To permit the zip to be unpacked by built-in extractor in WinXP and Server2003, WinZip 8, Java, and other older code,
            // you need to do one of the following: Specify UseZip64.Off, or set the Size.
            // If the file may be bigger than 4GB, or you do not need WinXP built-in compatibility, you do not need either,
            // but the zip will be in Zip64 format which not all utilities can understand.
            //   zipStream.UseZip64 = UseZip64.Off;
            newEntry.Size = fi.Length;

            zipStream.PutNextEntry(newEntry);

            // Zip the file in buffered chunks
            // the "using" will close the stream even if an exception occurs
            byte[] buffer = new byte[4096];
            using (FileStream streamReader = File.OpenRead(filename))
            {
                StreamUtils.Copy(streamReader, zipStream, buffer);
            }
            zipStream.CloseEntry();
        }
        string[] folders = Directory.GetDirectories(path);
        foreach (string folder in folders)
        {
            _compressFolder(folder, zipStream, folderOffset, excludes);
        }
    }

    #endregion

    #region Methods

    public static void CreateZipFromFolder(string folderToZipPath, string zipFullFilePath, List<string> excludes = null)
    {
        FileStream fsOut = File.Create(zipFullFilePath);
        ZipOutputStream zipStream = new ZipOutputStream(fsOut);

        zipStream.SetLevel(9); //0-9, 9 being the highest level of compression

        //zipStream.Password = password;  // optional. Null is the same as not setting. Required if using AES.

        // This setting will strip the leading part of the folder path in the entries, to
        // make the entries relative to the starting folder.
        // To include the full path for each entry up to the drive root, assign folderOffset = 0.
        int folderOffset = folderToZipPath.Length + (folderToZipPath.EndsWith("\\") ? 0 : 1);

        _compressFolder(folderToZipPath, zipStream, folderOffset, excludes);

        zipStream.IsStreamOwner = true// Makes the Close also Close the underlying stream
        zipStream.Close();
    }

    public static void ExtractZipFile(string zipFile, string destinationFolder, List<string> excludes = nullstring password = null)
    {
        ZipFile zf = null;
        try
        {
            FileStream fs = File.OpenRead(zipFile);
            zf = new ZipFile(fs);
            if (!String.IsNullOrEmpty(password))
            {
                zf.Password = password;     // AES encrypted entries are handled automatically
            }
            foreach (ZipEntry zipEntry in zf)
            {
                // Ignore directories
                if (!zipEntry.IsFile)
                    continue;

                if (excludes != null && zipEntry.IsFile)
                {
                    if (_ignore(excludes, zipEntry.Name)) continue;
                }

                String entryFileName = zipEntry.Name;
                // to remove the folder from the entry:- entryFileName = Path.GetFileName(entryFileName);
                // Optionally match entry names against a selection list here to skip as desired.
                // The unpacked length is available in the zipEntry.Size property.

                byte[] buffer = new byte[4096];     // 4K is optimum
                Stream zipStream = zf.GetInputStream(zipEntry);

                // Manipulate the output filename here as desired.
                String fullZipToPath = Path.Combine(destinationFolder, entryFileName);
                string directoryName = Path.GetDirectoryName(fullZipToPath);
                if (directoryName.Length > 0)
                    Directory.CreateDirectory(directoryName);

                // Unzip file in buffered chunks. This is just as fast as unpacking to a buffer the full size
                // of the file, but does not waste memory.
                // The "using" will close the stream even if an exception occurs.

                using (FileStream streamWriter = File.Create(fullZipToPath))
                {
                    StreamUtils.Copy(zipStream, streamWriter, buffer);
                }
            }
        }
        finally
        {
            if (zf != null)
            {
                zf.IsStreamOwner = true// Makes close also shut the underlying stream
                zf.Close(); // Ensure we release resources
            }
        }
    }

    #endregion
}