There are two options I found to be reliable and easy enough to be used, depending on the license type you are allowed to use in your project.
The first one is:
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
namespace DataReader.zip
{
internal static class Zip1 // SharpZipLib nuget lib, MIT free license
{
public static bool Create(string destZipPath, string folderToCompress, string? password = null, int compressionLevel = 3)
{
bool res;
try
{
var fsOut = File.Create($"{destZipPath}.zip"); // ending '.zip' is a must!
var zipStream = new ZipOutputStream(fsOut);
zipStream.SetLevel(compressionLevel); // 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.
var folderOffset = folderToCompress.Length + (folderToCompress.EndsWith("\\") ? 0 : 1);
res = CompressFolder(folderToCompress, zipStream, folderOffset);
zipStream.IsStreamOwner = true; // Makes the Close also Close the underlying stream
zipStream.Close();
return res;
}
catch (Exception e)
{
return Console.WriteLine($"{e.Message}: {destZipPath}");
}
}
private static bool CompressFolder(string path, ZipOutputStream zipStream, int folderOffset)
{
try
{
var files = Directory.GetFiles(path);
if (files.Length == 0)
{
Message.Show($"Warning: no files to compress found in folder '{path}'");
return false;
}
foreach (var filename in files)
{
var fi = new FileInfo(filename);
var 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
var 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.On;
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 (var streamReader = File.OpenRead(filename))
{
StreamUtils.Copy(streamReader, zipStream, buffer);
}
zipStream.CloseEntry();
}
return true;
}
catch (Exception e)
{
return Console.WriteLine($"{e.Message}: {destZipPath}");
}
}
}
}
And the second one:
using Ionic.Zip;
namespace DataReader.zip
{
internal class Zip2 // DotNetZip nuget lib
{
public static bool Create(string destZipPath, string folderToCompress, string? password = null, int compressionLevel = 3)
{
try
{
using ZipFile zip = new();
if (password != null)
zip.Password = password;
zip.CompressionLevel = (Ionic.Zlib.CompressionLevel)compressionLevel;
zip.AddDirectory(folderToCompress);
zip.Save($"{destZipPath}.zip");
return true;
}
catch (Exception e)
{
return Console.WriteLine($"{e.Message}: {destZipPath}");
}
}
}
}
With both options you will create a zip folder where a password will be reqzired to extract the files (if you have set it when calling the method).