LoginSignup
2
2

More than 5 years have passed since last update.

C#でSharpCompress+XZ.NETを使ってtar.xz圧縮/解凍をする

Last updated at Posted at 2018-02-23

非常に圧縮率の高いLZMAフォーマットを用いたファイル圧縮としてxzフォーマットがあります。LZMAフォーマットは7zipでも用いられていて、7zipもxzも同じぐらいファイルサイズが縮みます。xz自体はアーカイブではないので、gzipと同じようにtarで固めてから使います。

7zipと大きく違うところは、7zipがWindowsに特化したフォーマットであるのに対して、xzはLinux環境でも簡単に圧縮できます。OSコマンドからもできますし、Pythonのデフォルトのモジュールのtarfileを用いれば、バージョン3.3以降lzma圧縮ができるようになっているので、tar.gz、tar.bz2と同様にtar.xzも解凍できます(7zipはPythonで探しましたが、libarchiveのラッパーしかなくちょっと面倒くさい)。デフォルトのモジュールなので、Pythonさえ使えればルート権限なく高圧縮率のtar.xzが使えるはずです(レンタルサーバーで便利かもしれません)。

どのぐらい縮むのかというと、63MBのテキストが1.9MBまで縮みました(おおよそ1/33)。

C#から圧縮・解凍する場合として、tar用のライブラリとしてSharpCompressと、XZ用のライブラリとしてXZ.NETを用いる例を紹介します。他にLZMA SDK訂正:XZ Utilsからxz-5.2.3-windows.zipをダウンロードして、その中にあるliblzma.dllが必須になります。解凍するとアーキテクチャーが複数出てきますが、x86ビルドの場合は「bin_i686-sse2」のliblzma.dllを使いました。liblzma.dllをDebugやReleaseフォルダにコピーしておきます(参照の追加は不要です)。

環境:.NET Framework 4.5.2 / SharpCompress v0.19.2(2017/12/6) / XZ.NET v1.2.2(2015/10/28) / LZMA SDK v18.01(2018/1/28)

圧縮

using System.IO;
using SharpCompress.Writers;
using XZ.NET;

public static void Compress(string sourceDir, string destArchivePath)
{
    using (var outFileStream = new FileStream(destArchivePath, FileMode.Create, FileAccess.Write))
    using (var ms = new MemoryStream())
    {
        using (var tarWriter = WriterFactory.Open(ms, SharpCompress.Common.ArchiveType.Tar,
            new WriterOptions(SharpCompress.Common.CompressionType.None) { LeaveStreamOpen = true }))
        {
            tarWriter.WriteAll(sourceDir, "*", SearchOption.AllDirectories);
        }

        ms.Position = 0;
        using (var xzStream = new XZOutputStream(outFileStream))
        {
            var buf = new byte[1 * 1024 * 1024];
            var bytesRead = (long)0;

            while (bytesRead < ms.Length)
            {
                var count = ms.Read(buf, 0, buf.Length);
                xzStream.Write(buf, 0, count);
                bytesRead += count;
            }
        }
    }
}

バッファーを固定長ではなく、ms.ToArray()してバイナリをxzStreamにWriteさせるとエラーになったのでご注意ください。WinRARでもPythonでも読めたので特に問題ないかと思います。

フォルダに解凍

using System.IO;
using SharpCompress.Readers;
using XZ.NET;

public static void DecompressToDirectory(string sourceArchive, string destDirectory)
{
    using (var inFileStream = new FileStream(sourceArchive, FileMode.Open, FileAccess.Read))
    using (var xzStream = new XZInputStream(inFileStream))
    using (var ms = new MemoryStream())
    {
        xzStream.CopyTo(ms);
        ms.Position = 0;

        using (var tarReader = ReaderFactory.Open(ms))
        {
            while (tarReader.MoveToNextEntry())
            {
                if (!tarReader.Entry.IsDirectory)
                {
                    tarReader.WriteEntryToDirectory(destDirectory, new ExtractionOptions()
                    {
                        ExtractFullPath = true,
                        Overwrite = true
                    });
                }
            }
        }
    }
}

ごく普通の解凍です。

メモリ上に解凍

using System.IO;
using SharpCompress.Readers;
using XZ.NET;

public static void DecompressToMemory(string sourceArchive)
{
    using (var inFileStream = new FileStream(sourceArchive, FileMode.Open, FileAccess.Read))
    using (var xzStream = new XZInputStream(inFileStream))
    using (var tarMs = new MemoryStream())
    {
        xzStream.CopyTo(tarMs);
        tarMs.Position = 0;

        using (var tarReader = ReaderFactory.Open(tarMs))
        {
            while (tarReader.MoveToNextEntry())
            {
                if (!tarReader.Entry.IsDirectory)
                {
                    using(var entryMs = new MemoryStream())
                    using (var entryStream = tarReader.OpenEntryStream())
                    {
                        entryStream.CopyTo(entryMs);
                        var binary = entryMs.ToArray();
                        var str = Encoding.UTF8.GetString(binary);
                        //Console.WriteLine(str);
                    }
                }
            }
        }
    }
}

ダイレクトに変数に格納したいときなど。

その他注意

SharpCompressのドキュメント読んでたらこんなことが書いてありました。

XZ has known holes explained here: (http://www.nongnu.org/lzip/xz_inadequate.html) Use Tar/LZip for LZMA compression instead.

要するにXZは長期間の保存には不適切だ、LZIPをかわりに使えと。ただLZIPはPythonのデフォルトではインストールされていないので、モジュールのインストールにルート権限必要なのですよね。

tar.xzは転送量をケチってダウンロードさせたいときに結構便利なフォーマットだと思います。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2