在.NET中创建带密码的ZIP文件

30

我正在进行一个项目,需要使用C#从文件内容创建密码保护的zip文件。

之前我使用System.IO.Compression.GZipStream来创建gzip内容。 在.NET中有没有功能可以创建密码保护的zip或rar文件呢?


1
一般来说,如果 System.IO.Compression.GZipStream 的功能不足以满足您的需求,那么 https://sevenzipsharp.codeplex.com 框架是一个更为复杂的选择。在 .net 框架中,GZipStream 是创建归档文件的唯一方式。 - VitaliyK
@VitaliyK 我也会推荐7Zip#,但是除了GZipStream之外,框架中还有其他的压缩机制(例如DeflateStreamZipPackageZipFile(自4.5版本以来)等)。 - Manfred Radlwimmer
@VitaliyK,gzipstream有密码功能吗?我没有找到任何密码功能。 - Hamed_gibago
@Hamed_gibago 不,你不能使用GZipStream来对存档进行密码保护。你需要使用其他的压缩框架(如7Zip、DotNetZip等)。 - VitaliyK
5个回答

15

请看DotNetZip(@AFract在评论中提供了新的GitHub链接)。

它有非常好的文档,并且还允许您将dll作为嵌入式文件在运行时加载。


这个项目还在维护吗?原始项目有一个 Github 分支,但我经常不确定像那样的项目的状态,因为 CodePlex 已关闭。 - StayOnTarget
2
https://www.nuget.org/packages/DotNetZip '上次更新于3个月前',我认为它仍在维护。 - Jan Wiesemann
请提供示例。 - Demodave
@Demodave,请查看DotNetZip存储库上的示例。https://github.com/haf/DotNetZip.Semverd/tree/master/src/Examples/C# - Jan Wiesemann
@PaulEfford 欢迎来到ZIP...只有实际内容被加密,但索引表没有。这就是为什么您能够看到存在哪些文件。新版本应该支持它,但我不确定是否在任何地方使用。一个解决方法是嵌套两个zip文件。外部文件将设置密码,而内部文件可以存储而不加密。但是,请不要这样做...还有其他工具和格式可供选择... - Jan Wiesemann
显示剩余3条评论

8
很抱歉,框架中没有这样的功能。有一种方法可以制作ZIP文件,但没有密码。如果您想在C#中创建受密码保护的ZIP文件,我建议使用SevenZipSharp。它基本上是7-Zip的托管包装器。
SevenZipBase.SetLibraryPath(Path.Combine(
        Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Environment.CurrentDirectory,
        "7za.dll"));

SevenZipCompressor compressor = new SevenZipCompressor();

compressor.Compressing += Compressor_Compressing;
compressor.FileCompressionStarted += Compressor_FileCompressionStarted;
compressor.CompressionFinished += Compressor_CompressionFinished;

string password = @"whatever";
string destinationFile = @"C:\Temp\whatever.zip";
string[] sourceFiles = Directory.GetFiles(@"C:\Temp\YourFiles\");

if (String.IsNullOrWhiteSpace(password))
{
    compressor.CompressFiles(destinationFile, sourceFiles);
}
else
{
    //optional
    compressor.EncryptHeaders = true;
    compressor.CompressFilesEncrypted(destinationFile, password, sourceFiles);
}

很遗憾,这个解决方案只适用于Windows,因为它依赖于7za.dll。其他解决方案可以跨平台使用。 - frankhommers

6

DotNetZip 在使用上非常顺畅且干净。

DotNetZip is a FAST, FREE class library and toolset for manipulating zip files.

代码

static void Main(string[] args)
{
        using (ZipFile zip = new ZipFile())
        {

            zip.Password = "mypassword";

            zip.AddDirectory(@"C:\Test\Report_CCLF5\");
            zip.Save(@"C:\Test\Report_CCLF5_PartB.zip");
        }
 }

它创建了一个zip文件,您可以在其中查看文件;要打开其中任何一个文件,都需要输入密码。但非常有趣的是,您可以提取zip文件并获取其中所有文件...而无需输入任何密码!那么...这个“密码”功能的真正用途是什么呢? - Paul Efford

5

我希望能提供更多的替代方案。

对于.NET,可以使用SharpZipLib,对于Xamarin,则可使用SharpZipLib.Portable

.NET的示例:

using ICSharpCode.SharpZipLib.Zip;

// Compresses the supplied memory stream, naming it as zipEntryName, into a zip,
// which is returned as a memory stream or a byte array.
//
public MemoryStream CreateToMemoryStream(MemoryStream memStreamIn, string zipEntryName) {

    MemoryStream outputMemStream = new MemoryStream();
    ZipOutputStream zipStream = new ZipOutputStream(outputMemStream);

    zipStream.SetLevel(3); //0-9, 9 being the highest level of compression
    zipStream.Password = "Your password";

    ZipEntry newEntry = new ZipEntry(zipEntryName);
    newEntry.DateTime = DateTime.Now;

    zipStream.PutNextEntry(newEntry);

    StreamUtils.Copy(memStreamIn, zipStream, new byte[4096]);
    zipStream.CloseEntry();

    zipStream.IsStreamOwner = false;    // False stops the Close also Closing the underlying stream.
    zipStream.Close();          // Must finish the ZipOutputStream before using outputMemStream.

    outputMemStream.Position = 0;
    return outputMemStream;

    // Alternative outputs:
    // ToArray is the cleaner and easiest to use correctly with the penalty of duplicating allocated memory.
    byte[] byteArrayOut = outputMemStream.ToArray();

    // GetBuffer returns a raw buffer raw and so you need to account for the true length yourself.
    byte[] byteArrayOut = outputMemStream.GetBuffer();
    long len = outputMemStream.Length;
}

更多示例可以在这里找到。

如果您可以不使用密码功能,可以提及ZipStorer或内置在System.IO.Compression中的.NET函数。


0

我发现有两个选项是可靠且易于使用的,具体取决于您在项目中被允许使用的许可证类型。

第一个选项是:

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}");
        }
    }
}
}

还有第二个:

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}");
            }
        }
    }
}

使用这两个选项,您将创建一个zip文件夹,在提取文件时需要输入密码(如果在调用方法时设置了密码)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接