以编程方式创建普通zip文件

53

我看过很多关于如何在c#中压缩单个文件的教程。但是我需要能够将不止一个文件创建为普通的*.zip文件。.NET中是否有可以做到这一点的东西?你会有什么建议(请注意,我受到严格规定,不能使用其他库)。

谢谢


使用.NET的ZipArchive.CreateEntryFromFile时,请注意您的参数,并确保不要在entryName参数中使用绝对路径。您将不会收到警告,zip文件将被创建,但是它将是空的。如果您正在处理字符串而不是.NET的文件系统对象,则可以使用Path.GetFileName(filePath),这将允许使用obj.Name - kayleeFrye_onDeck
11个回答

72

对于其他人偶然发现这个问题,我想更新一下。

从.NET 4.5开始,你可以使用System.IO.Compression压缩一个目录成为zip文件。需要注意的是,默认情况下没有引用System.IO.Compression.FileSystem ,需要手动添加引用。然后,你可以编写以下代码:

System.IO.Compression.ZipFile.CreateFromDirectory(dirPath, zipFile);

唯一可能的问题是,此程序集不适用于 Windows Store 应用程序。


也没有密码保护的支持。 - SpoiledTechie.com

56
现在您可以使用ZipArchive类(System.IO.Compression.ZipArchive),该类可以从.NET 4.5中获取。
您需要将System.IO.Compression添加为引用。
示例:生成PDF文件的zip。
using (var fileStream = new FileStream(@"C:\temp\temp.zip", FileMode.CreateNew))
{
    using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true))
    {
        foreach (var creditNumber in creditNumbers)
        {
            var pdfBytes = GeneratePdf(creditNumber);
            var fileName = "credit_" + creditNumber + ".pdf";
            var zipArchiveEntry = archive.CreateEntry(fileName, CompressionLevel.Fastest);
            using (var zipStream = zipArchiveEntry.Open())
                zipStream.Write(pdfBytes, 0, pdfBytes.Length);
            }
        }
    }
}

1
如果您正在使用VB.NET,获取文件字节的最快方法是:My.Computer.FileSystem.ReadAllBytes(f) - Janeks Bergs
这与System.IO.File.ReadAllBytes有何不同? - LordWabbit

32

我的看法:

    using (ZipArchive archive = ZipFile.Open(zFile, ZipArchiveMode.Create))
    {
        foreach (var fPath in filePaths)
        {
            archive.CreateEntryFromFile(fPath,Path.GetFileName(fPath));
        }
    }

因此,Zip文件可以直接从文件/目录创建。


1
@RodrigoStrauss 是 (dest, source)。 - Guy L
ZipArchive是从哪个引用中来的?C#无法识别它。 - GuidoG
4
ZipFile和ZipArchive来自于System.IO.Compression.FileSystem和System.IO.Compression程序集。 - LotuX
5
这个库只能在.NET Framework 4.5及以上版本中使用。 - Håkon Seljåsen
1
这对我来说是最简洁的答案。+1 - AndyUK

20

编辑:如果你使用的是 .Net 4.5 或更高版本,这已经内置在框架中了,点击此处查看。

对于早期版本或需要更多控制的情况,你可以使用 Windows 的 shell 函数,如这篇由 Gerald Gibson Jr 在 CodeProject 上列出的文章中所述

我已复制原始文章文本(原始许可证:公共领域):

使用 Windows Shell API 和 C# 压缩 Zip 文件

enter image description here

介绍

本文是之前我写的一篇有关解压缩 Zip 文件的文章的后续。使用这个代码,你可以在 C# 中使用 Windows Shell API 压缩 Zip 文件,并且不需要显示上面的复制进度窗口。通常情况下,当你使用 Shell API 来压缩 Zip 文件时,即使你设置选项告诉 Windows 不显示它,它也会显示一个复制进度窗口。为了避免这种情况,你需要将 Shell API 代码移到一个单独的可执行文件中,然后使用 .NET Process 类启动该可执行文件,并确保将进程窗口样式设置为“隐藏”。

背景

是否曾经需要压缩 Zip 文件,而需要比许多免费压缩库提供更好的 Zip?例如,你需要压缩文件夹和子文件夹以及文件。Windows 压缩可以压缩不止单个文件。你只需要一种方法来以编程方式让 Windows 静默压缩这些 Zip 文件。当然,你可以花 $300 购买商业 Zip 组件之一,但如果你只需要压缩文件夹层次结构,那么免费的方式是难以被超越的。

使用代码

下面的代码展示了如何使用 Windows Shell API 压缩一个 Zip 文件。首先创建一个空的 Zip 文件。要做到这一点,需要创建一个正确构造的字节数组,然后将该数组保存为一个扩展名为“.zip”的文件。我是怎么知道要放哪些字节到数组中的呢?我只是使用 Windows 创建了一个包含单个文件压缩的 Zip 文件。然后我用 Windows 打开了这个 Zip 文件并删除了压缩的文件。这样就留下了一个空的 Zip。接下来,我在十六进制编辑器(Visual Studio)中打开了这个空的 Zip 文件,并查看了十六进制字节值,并使用 Windows 计算器将它们转换为十进制,并将这些十进制值复制到我的字节数组代码中。源文件夹指向你要压缩的文件夹。目标文件夹指向你刚刚创建的空的 Zip 文件。这段代码会压缩 Zip 文件,但是它也会显示复制进度窗口。为了让这段代码工作,还需要设置对 COM 库的引用。在“引用”窗口中,转到 COM 选项卡,并选择标记为“Microsoft Shell Controls and Automation”的库。

//Create an empty zip file
byte[] emptyzip = new byte[]{80,75,5,6,0,0,0,0,0, 
                  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

FileStream fs = File.Create(args[1]);
fs.Write(emptyzip, 0, emptyzip.Length);
fs.Flush();
fs.Close();
fs = null;

//Copy a folder and its contents into the newly created zip file
Shell32.ShellClass sc = new Shell32.ShellClass();
Shell32.Folder SrcFlder = sc.NameSpace(args[0]);
Shell32.Folder DestFlder = sc.NameSpace(args[1]); 
Shell32.FolderItems items = SrcFlder.Items();
DestFlder.CopyHere(items, 20);

//Ziping a file using the Windows Shell API 
//creates another thread where the zipping is executed.
//This means that it is possible that this console app 
//would end before the zipping thread 
//starts to execute which would cause the zip to never 
//occur and you will end up with just
//an empty zip file. So wait a second and give 
//the zipping thread time to get started
System.Threading.Thread.Sleep(1000);

这篇文章提供的示例解决方案展示了如何将代码放入控制台应用程序中,并启动此控制台应用程序以压缩Zip文件,而无需显示复制进度窗口。下面的代码展示了一个按钮点击事件处理程序,其中包含用于启动控制台应用程序的代码,以便在压缩过程中没有用户界面:
private void btnUnzip_Click(object sender, System.EventArgs e)
{
    //Test to see if the user entered a zip file name
    if(txtZipFileName.Text.Trim() == "")
    {
        MessageBox.Show("You must enter what" + 
               " you want the name of the zip file to be");
        //Change the background color to cue the user to what needs fixed
        txtZipFileName.BackColor = Color.Yellow;
        return;
    }
    else
    {
        //Reset the background color
        txtZipFileName.BackColor = Color.White;
    }

    //Launch the zip.exe console app to do the actual zipping
    System.Diagnostics.ProcessStartInfo i =
        new System.Diagnostics.ProcessStartInfo(
        AppDomain.CurrentDomain.BaseDirectory + "zip.exe");
    i.CreateNoWindow = true;
    string args = "";

    
    if(txtSource.Text.IndexOf(" ") != -1)
    {
        //we got a space in the path so wrap it in double qoutes
        args += "\"" + txtSource.Text + "\"";
    }
    else
    {
        args += txtSource.Text;
    }

    string dest = txtDestination.Text;

    if(dest.EndsWith(@"\") == false)
    {
        dest += @"\";
    }

    //Make sure the zip file name ends with a zip extension
    if(txtZipFileName.Text.ToUpper().EndsWith(".ZIP") == false)
    {
        txtZipFileName.Text += ".zip";
    }

    dest += txtZipFileName.Text;

    if(dest.IndexOf(" ") != -1)
    {
        //we got a space in the path so wrap it in double qoutes
        args += " " + "\"" + dest + "\"";
    }
    else
    {
        args += " " + dest;
    }

    i.Arguments = args;
    

    //Mark the process window as hidden so 
    //that the progress copy window doesn't show
    i.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;    
    System.Diagnostics.Process p = System.Diagnostics.Process.Start(i);
    p.WaitForExit();
    MessageBox.Show("Complete");
}

5
自 .NET 4.5 开始,您可以使用 System.IO.Compression.ZipFile 来实现此操作;有关更多详细信息,请参阅此答案 - Matt

20
以下是几个你可以考虑的资源: 在.NET中创建Zip归档文件(无需像SharpZipLib这样的外部库)

使用System.IO.Packaging压缩流

我的建议和偏好是使用system.io.packacking。这可以减少依赖项(只需要框架)。 Jgalloway的帖子(第一个参考)提供了将两个文件添加到zip文件的良好示例。是的,它可能更冗长,但您可以轻松创建一个外观(在某种程度上,他的AddFileToZip就是这样)。 希望对你有所帮助。

6
您可以尝试使用SharpZipLib来实现。它是一个开源的、平台无关的纯C#代码库。

5

.NET具有压缩文件的内置功能,位于System.IO.Compression命名空间中。使用此功能,您无需作为依赖项使用额外的库。此功能自.NET 2.0起可用。

以下是我链接的MSDN页面上执行压缩的方法:

    public static void Compress(FileInfo fi)
    {
        // Get the stream of the source file.
        using (FileStream inFile = fi.OpenRead())
        {
            // Prevent compressing hidden and already compressed files.
            if ((File.GetAttributes(fi.FullName) & FileAttributes.Hidden)
                    != FileAttributes.Hidden & fi.Extension != ".gz")
            {
                // Create the compressed file.
                using (FileStream outFile = File.Create(fi.FullName + ".gz"))
                {
                    using (GZipStream Compress = new GZipStream(outFile,
                            CompressionMode.Compress))
                    {
                        // Copy the source file into the compression stream.
                        byte[] buffer = new byte[4096];
                        int numRead;
                        while ((numRead = inFile.Read(buffer, 0, buffer.Length)) != 0)
                        {
                            Compress.Write(buffer, 0, numRead);
                        }
                        Console.WriteLine("Compressed {0} from {1} to {2} bytes.",
                            fi.Name, fi.Length.ToString(), outFile.Length.ToString());
                    }
                }
            }
        }

这是针对单个文件的。如何处理多个文件? - GaneshKumar

4

您应该查看Zip Packages

它们是普通ZIP存档的更结构化版本,在根目录中需要一些元数据。因此,其他ZIP工具可以打开包,但System.IO.Packaging API无法打开所有ZIP文件。


1
这是我在参考上述帖子后编写的代码。感谢你们的帮助。
此代码接受文件路径列表并将它们压缩成一个zip文件。
public class Zip
{
    private string _filePath;
    public string FilePath { get { return _filePath; } }

    /// <summary>
    /// Zips a set of files
    /// </summary>
    /// <param name="filesToZip">A list of filepaths</param>
    /// <param name="sZipFileName">The file name of the new zip (do not include the file extension, nor the full path - just the name)</param>
    /// <param name="deleteExistingZip">Whether you want to delete the existing zip file</param>
    /// <remarks>
    /// Limitation - all files must be in the same location. 
    /// Limitation - must have read/write/edit access to folder where first file is located.
    /// Will throw exception if the zip file already exists and you do not specify deleteExistingZip
    /// </remarks>
    public Zip(List<string> filesToZip, string sZipFileName, bool deleteExistingZip = true)
    {
        if (filesToZip.Count > 0)
        {
            if (File.Exists(filesToZip[0]))
            {

                // Get the first file in the list so we can get the root directory
                string strRootDirectory = Path.GetDirectoryName(filesToZip[0]);

                // Set up a temporary directory to save the files to (that we will eventually zip up)
                DirectoryInfo dirTemp = Directory.CreateDirectory(strRootDirectory + "/" + DateTime.Now.ToString("yyyyMMddhhmmss"));

                // Copy all files to the temporary directory
                foreach (string strFilePath in filesToZip)
                {
                    if (!File.Exists(strFilePath))
                    {
                        throw new Exception(string.Format("File {0} does not exist", strFilePath));
                    }
                    string strDestinationFilePath = Path.Combine(dirTemp.FullName, Path.GetFileName(strFilePath));
                    File.Copy(strFilePath, strDestinationFilePath);
                }

                // Create the zip file using the temporary directory
                if (!sZipFileName.EndsWith(".zip")) { sZipFileName += ".zip"; }
                string strZipPath = Path.Combine(strRootDirectory, sZipFileName);
                if (deleteExistingZip == true && File.Exists(strZipPath)) { File.Delete(strZipPath); }
                ZipFile.CreateFromDirectory(dirTemp.FullName, strZipPath, CompressionLevel.Fastest, false);

                // Delete the temporary directory
                dirTemp.Delete(true);

                _filePath = strZipPath;                    
            }
            else
            {
                throw new Exception(string.Format("File {0} does not exist", filesToZip[0]));
            }
        }
        else
        {
            throw new Exception("You must specify at least one file to zip.");
        }
    }
}

1
这可以通过添加对System.IO.Compression和System.IO.Compression.Filesystem的引用来完成。
一个示例createZipFile()方法可能如下所示:
public static void createZipFile(string inputfile, string outputfile, CompressionLevel compressionlevel)
        {
            try
            {
                using (ZipArchive za = ZipFile.Open(outputfile, ZipArchiveMode.Update))
                {
                    //using the same file name as entry name
                    za.CreateEntryFromFile(inputfile, inputfile);
                }
            }
            catch (ArgumentException)
            {
                Console.WriteLine("Invalid input/output file.");
                Environment.Exit(-1);
            }
}

where

  • inputfile= 文件名,需要压缩的文件(需包含扩展名)
  • outputfile= 目标 zip 文件名

有关 MSDN 中 ZipFile 类的更多详细信息


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