【C#】ZipArchiveでフォルダを扱う

.NET の Zip を扱うための ZipArchive クラスってファイルを zip に追加するための「CreateEntryFromFile」はありますが、フォルダを扱う「CreateEntryFromDireocty」みたいな操作はありません。

普通に使用したらすぐ必要になるので、ZipArchive でフォルダを扱えるように

作成環境

  • .NET6
  • Windows11
  • VisualStudio2022

実装コード

ZipArchiveExtensionsクラス

拡張メソッドとして CreateEntryFromAny メソッドを定義します。

これにフォルダを指定すると ZipArchive に再帰的にすべてのフォルダとファイルが追加されます。

ファイルも自動判別してそのまま追加できます。

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

/// <summary>
/// <see cref="ZipArchive"/> の機能を拡張します。
/// </summary>
public static class ZipArchiveExtensions
{
    //
    // Public Methods
    // - - - - - - - - - - - - - - - - - - - -

    /// <summary>
    /// ファイルもしくはディレクトリをアーカイブに追加します。
    /// </summary>
    /// <remarks>
    /// ディレクトリは再帰的に追加されます。
    /// </remarks>
    public static void CreateEntryFromAny(this ZipArchive archive, 
        string sourceName, 
        string entryName = "", 
        CompressionLevel compressionLevel = CompressionLevel.Optimal)
    {
        if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory))
        {
            archive.CreateEntryFromDirectoryCore(sourceName, entryName, compressionLevel);
        }
        else
        {
            archive.CreateEntryFromFile(sourceName, entryName, compressionLevel);
        }
    }

    //
    // Private Methods
    // - - - - - - - - - - - - - - - - - - - -

    private static void CreateEntryFromAnyCore(this ZipArchive archive, 
        string sourceName, 
        string entryName = "", 
        CompressionLevel compressionLevel = CompressionLevel.Optimal)
    {
        if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory))
        {
            archive.CreateEntryFromDirectoryCore(sourceName, 
                Path.Combine(entryName, Path.GetFileName(sourceName)), compressionLevel);
        }
        else
        {
            archive.CreateEntryFromFile(sourceName, 
                Path.Combine(entryName, Path.GetFileName(sourceName)), compressionLevel);
        }
    }

    private static void CreateEntryFromDirectoryCore(this ZipArchive archive, 
        string sourceDirName, 
        string entryName = "", 
        CompressionLevel compressionLevel = CompressionLevel.Optimal)
    {
        string[] entries = Directory.GetFileSystemEntries(sourceDirName);
        if (entries.Length != 0)
        {
            foreach (var entry in entries)
            {
                archive.CreateEntryFromAnyCore(entry, entryName);
            }
        }
        else
        {
            if (!string.IsNullOrEmpty(entryName) && entryName[entryName.Length - 1] != '/')
            {
                entryName += "\\";
            }
            archive.CreateEntry(entryName);
        }
    }
}

使い方

使い方は以下の通り。

CreateEntryFromAny はファイルとフォルダを自動的に判定するのでどちらでも追加可能です。

private static void Main(string[] args)
{
    string filePath = @"C:\temp\Archive.zip";

    if (File.Exists(filePath))
    {
        File.Delete(filePath);
    }

    using (ZipArchive archive = ZipFile.Open(filePath, ZipArchiveMode.Create))
    {
        archive.CreateEntryFromAny(@"D:\Sample"); // フォルダの追加
        archive.CreateEntryFromAny(@"D:\Sample\text.txt"); // ファイルの追加
    };
}

大抵どのバージョンの .NET でも同じようにコードは使用できると思います。

関連記事

takap-tech.com

参考サイト

この記事は以下のコードを参考に書きました。

stackoverflow.com