Creating Directories in a ZipArchive C# .Net 4.5

Solution 1:

You can use something like the following, in other words, create the directory structure by hand:

using (var fs = new FileStream("1.zip", FileMode.Create))
using (var zip = new ZipArchive(fs, ZipArchiveMode.Create))
{
    zip.CreateEntry("12/3/"); // just end with "/"
}

Solution 2:

I know I'm late to the party (7.25.2018),

this works flawlessly to me, even with recursive directories.

Firstly, remember to install the NuGet package:

Install-Package System.IO.Compression

And then, Extension file for ZipArchive:

 public static class ZipArchiveExtension {

     public static void CreateEntryFromAny(this ZipArchive archive, string sourceName, string entryName = "") {
         var fileName = Path.GetFileName(sourceName);
         if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory)) {
             archive.CreateEntryFromDirectory(sourceName, Path.Combine(entryName, fileName));
         } else {
             archive.CreateEntryFromFile(sourceName, Path.Combine(entryName, fileName), CompressionLevel.Fastest);
         }
     }

     public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, string entryName = "") {
         string[] files = Directory.GetFiles(sourceDirName).Concat(Directory.GetDirectories(sourceDirName)).ToArray();
         archive.CreateEntry(Path.Combine(entryName, Path.GetFileName(sourceDirName)));
         foreach (var file in files) {
             archive.CreateEntryFromAny(file, entryName);
         }
     }
 }

And then you can pack anything, whether it is file or directory:

using (var memoryStream = new MemoryStream()) {
    using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
        archive.CreateEntryFromAny(sourcePath);
    }
}

Solution 3:

If you are working on a project that can use full .NET you may try to use the ZipFile.CreateFromDirectory method, as explained here:

using System;
using System.IO;
using System.IO.Compression;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            string startPath = @"c:\example\start";
            string zipPath = @"c:\example\result.zip";
            string extractPath = @"c:\example\extract";

            ZipFile.CreateFromDirectory(startPath, zipPath, CompressionLevel.Fastest, true);

            ZipFile.ExtractToDirectory(zipPath, extractPath);
        }
    }
}

Of course this will only work if you are creating new Zips based on a given directory.

As per the comment, the previous solution does not preserve the directory structure. If that is needed, then the following code might address that:

    var InputDirectory = @"c:\example\start";
    var OutputFilename = @"c:\example\result.zip";
    using (Stream zipStream = new FileStream(Path.GetFullPath(OutputFilename), FileMode.Create, FileAccess.Write))
    using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
    {
        foreach(var filePath in System.IO.Directory.GetFiles(InputDirectory,"*.*",System.IO.SearchOption.AllDirectories))
        {
            var relativePath = filePath.Replace(InputDirectory,string.Empty);
            using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
            using (Stream fileStreamInZip = archive.CreateEntry(relativePath).Open())
                fileStream.CopyTo(fileStreamInZip);
        }
    }

Solution 4:

Here is one possible solution:

public static class ZipArchiveExtension
{
    public static ZipArchiveDirectory CreateDirectory(this ZipArchive @this, string directoryPath)
    {
        return new ZipArchiveDirectory(@this, directoryPath);
    }
}

public class ZipArchiveDirectory
{
    private readonly string _directory;
    private ZipArchive _archive;

    internal ZipArchiveDirectory(ZipArchive archive, string directory)
    {
        _archive = archive;
        _directory = directory;
    }

    public ZipArchive Archive { get{return _archive;}}

    public ZipArchiveEntry CreateEntry(string entry)
    {
        return _archive.CreateEntry(_directory + "/" + entry);
    }

    public ZipArchiveEntry CreateEntry(string entry, CompressionLevel compressionLevel)
    {
        return _archive.CreateEntry(_directory + "/" + entry, compressionLevel);
    }
}

and used:

var directory = _archive.CreateDirectory(context);
var entry = directory.CreateEntry(context);
var stream = entry.Open();

but I can foresee problems with nesting, perhaps.