非常に圧縮率の高い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は転送量をケチってダウンロードさせたいときに結構便利なフォーマットだと思います。