using Unity.SharpZipLib.Core;
using Unity.SharpZipLib.Zip;
using System;
using System.IO;
namespace Unity.SharpZipLib.Utils {
///
/// Provides utility methods for compressing and decompressing zip files.
///
///
/// This class provides facilities to compress entire folders into a zip file and to uncompress a zip file into folders.
///
///
/// The following example adds a menu item named "CompressAndUncompress" to Debug in the menu bar that compresses the Assets folder to UnityAssets.zip
/// and extracts the contents back.
///
///
/// using System.IO;
/// using Unity.SharpZipLib.Utils;
/// using UnityEditor;
/// using UnityEngine;
///
/// public static class DebugMenu {
/// [MenuItem("Debug/CompressAndUncompress")]
/// static void CompressAndUncompress() {
///
/// //Compress
/// string folderToCompress = "Assets";
/// string zipPath = Path.Combine(Application.temporaryCachePath, "UnityAssets.zip");
/// ZipUtility.CompressFolderToZip(zipPath,null, folderToCompress);
/// Debug.Log($"{folderToCompress} folder compressed to: " + zipPath);
///
/// //Uncompress
/// string extractPath = Path.Combine(Application.temporaryCachePath, "UnityAssetsExtracted");
/// ZipUtility.UncompressFromZip(zipPath, null, extractPath);
/// Debug.Log($"Uncompressed to: " + extractPath);
/// }
/// }
///
///
public static class ZipUtility {
///
/// Uncompress the contents of a zip file into the specified folder.
///
/// The path to the zip file to be extracted
/// The password required to open the zip file. Set to null if the zip file is not encrypted
/// The output folder where the contents will be extracted
///
/// If the output folder already exists, its contents will be deleted before the extraction.
///
///
///
///
public static void UncompressFromZip(string archivePath, string password, string outFolder) {
if (Directory.Exists(outFolder)) {
Directory.Delete(outFolder,true);
}
Directory.CreateDirectory(outFolder);
using(Stream fs = File.OpenRead(archivePath))
using(ZipFile zf = new ZipFile(fs)){
if (!String.IsNullOrEmpty(password)) {
// AES encrypted entries are handled automatically
zf.Password = password;
}
foreach (ZipEntry zipEntry in zf) {
if (!zipEntry.IsFile) {
// Ignore directories
continue;
}
String entryFileName = zipEntry.Name;
// to remove the folder from the entry:
//entryFileName = Path.GetFileName(entryFileName);
// Optionally match entrynames against a selection list here
// to skip as desired.
// The unpacked length is available in the zipEntry.Size property.
// Manipulate the output filename here as desired.
var fullZipToPath = Path.Combine(outFolder, entryFileName);
var directoryName = Path.GetDirectoryName(fullZipToPath);
if (directoryName.Length > 0) {
Directory.CreateDirectory(directoryName);
}
// 4K is optimum
var buffer = new byte[4096];
// 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(Stream zipStream = zf.GetInputStream(zipEntry))
using (Stream fsOutput = File.Create(fullZipToPath)) {
StreamUtils.Copy(zipStream, fsOutput, buffer);
}
}
}
}
//---------------------------------------------------------------------------------------------------------------------
///
/// Creates a zip file on disk containing the contents of the nominated folder.
///
/// The path where the created zip file will be saved
/// The password required to open the zip file. Set to null if not required
/// The folder to be compressed
///
/// This method recursively compresses all files within the specified folder.
/// You can specify a password to encrypt the target zip file.
///
///
///
///
public static void CompressFolderToZip(string outPathname, string password, string folderName) {
using(FileStream fsOut = File.Create(outPathname))
using(var zipStream = new ZipOutputStream(fsOut)) {
//0-9, 9 being the highest level of compression
zipStream.SetLevel(3);
// optional. Null is the same as not setting. Required if using AES.
zipStream.Password = password;
// 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 to 0.
int folderOffset = folderName.Length + (folderName.EndsWith("\\") ? 0 : 1);
CompressFolderToZipInternal(folderName, zipStream, folderOffset);
}
}
//---------------------------------------------------------------------------------------------------------------------
// Recursively compresses a folder structure
private static void CompressFolderToZipInternal(string path, ZipOutputStream zipStream, int folderOffset) {
var files = Directory.GetFiles(path);
foreach (var filename in files) {
var fi = new FileInfo(filename);
// Make the name in zip based on the folder
var entryName = filename.Substring(folderOffset);
// Remove drive from name and fixe slash direction
entryName = ZipEntry.CleanName(entryName);
var newEntry = new ZipEntry(entryName);
// Note the zip format stores 2 second granularity
newEntry.DateTime = fi.LastWriteTime;
// 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
var buffer = new byte[4096];
using (FileStream fsInput = File.OpenRead(filename)) {
StreamUtils.Copy(fsInput, zipStream, buffer);
}
zipStream.CloseEntry();
}
// Recursively call CompressFolder on all folders in path
var folders = Directory.GetDirectories(path);
foreach (var folder in folders) {
CompressFolderToZipInternal(folder, zipStream, folderOffset);
}
}
}
} //end namespace