JavaでBrotli(ブロートリ)を使う

Java Brotli 圧縮する方法を紹介します。

Brotli ブロートリ 2015 年に Google が発表した圧縮アルゴリズムおよびその実装です。zlib よりも 15~25%くらい圧縮率が高くなります。LZMA と比べてもわずかに圧縮率が高いです。

Brotli の展開には専用のデコーダーが必要になりますが すでに Chrome Firefox Edge Safari Opera など主要なブラウザーが Brotli をサポートしています。

Internet Explorer IE など一部のブラウザーは Brotli に対応していませんし Chrome HTTP 接続時は Accept-Encoding br を含めないようです。HTTPS 接続でないと Brotli を使用できないということです Web サーバーはもうしばらく gzip 圧縮のサポートを続けたほうがよいでしょう。

読み方を教えて?
ブロートリと読みます。brot ブロート はパンを意味するドイツ語だそうです。li は英語の let みたいに小さな感じを表わします。なので brotli は小さなパンといった意味になりますね。

JNI 版を使おう

Java では Brotli 公式の JNI バインディングが使用できます。

他にも Java のみで実装された Brotli ライブラリとして BrotliHaxe がありますが性能はイマイチなようです。BrotliHaxe のサイトに掲載されているベンチマーク結果によると Java 版はオリジナルの C 言語版に比べて 15 倍くらいの時間が掛かっています。Java の名誉のために言っておきますが これでも Java はマシなほうです。C# Python PHP 5 ではさらに時間が掛かっています

オリジナルの Brotli 開発者である eustas さんは Brotli のエンコーダーは Java に移植されますか? との問い掛けにこう答えています。

Currently we have no plans for Java encoder port. But, for sure, we will have JNI bindings. Encoder is complex and we try to use all the power of CPU to be competitive. With Java it is much harder to match plain C performance.

エンコーダーを Java に移植する計画はありませんが JNI バインディングはあります。エンコーダーは複雑です。私たちは性能を出すために CPU パワーをフル活用しようとしています。Java C と同じパフォーマンスを出すのは難しいのです

Pure Java 実装にこだわらず 素直に JNI 版を使うのが良さそうです。

Brotli の公式サイトではビルド済みの JNI ライブラリを提供していないようです。代わりに下記のサイトから JNI ライブラリをダウンロードしましょう。

以下 Windows 環境について説明します。Mac Linux についても同様の手順で使えるようになると思います

私は brotli-win-bazel-jni-2019-05-03.zip をダウンロードしました。これは Brotli のバージョン 1.0.7 をビルドしたものです。ZIP ファイルを展開すると中には brotli_jni.dll が入っていました。これは 64 ビット版の DLL です

DLL のみで JAR が含まれていませんね。仕方がないので JAR は自分でビルドします。ライブラリ本体ではなく小さなラッパーなので JAR のビルド自体は難しくありません。依存ライブラリもなく JDK さえあればビルドできます。

ここから v1.0.7 のソースコードをダウンロードしてビルドしました。ビルドに使用した Gradle プロジェクトを参考に置いておきます。

中には Gradle プロジェクト一式が入っています。

JDK PATH が通っている環境であれば build.bat をダブルクリックするだけでビルドできます。そうでなければ コマンドプロンプトを起動して JDK PATH を通してから以下のコマンドを実行します。

gradlew build

これで Brotli v1.0.7 のソースコードのダウンロード 展開 ビルドまで自動でおこなわれます。

コマンドプロンプト
C:¥temp¥brotli-build-script>gradlew build > Configure project : C:¥Users¥user¥.gradle¥caches¥modules-2¥files-2.1¥google¥brotli¥1.0.7¥8737bcfb36f1318d4461181523085a239e9fb9a6¥brotli-1.0.7.zip > Task :compileJava C:¥temp¥brotli-build-script¥download¥brotli-1.0.7¥java¥org¥brotli¥wrapper¥dec¥DecoderJNI.java:110: 警告:[deprecation] Objectのfinalize()は推奨されま せん protected void finalize() throws Throwable { ^ C:¥temp¥brotli-build-script¥download¥brotli-1.0.7¥java¥org¥brotli¥wrapper¥dec¥DecoderJNI.java:115: 警告:[deprecation] Objectのfinalize()は推奨されま せん super.finalize(); ^ C:¥temp¥brotli-build-script¥download¥brotli-1.0.7¥java¥org¥brotli¥wrapper¥enc¥EncoderJNI.java:104: 警告:[deprecation] Objectのfinalize()は推奨されま せん protected void finalize() throws Throwable { ^ C:¥temp¥brotli-build-script¥download¥brotli-1.0.7¥java¥org¥brotli¥wrapper¥enc¥EncoderJNI.java:109: 警告:[deprecation] Objectのfinalize()は推奨されま せん super.finalize(); ^ 警告4個 Deprecated Gradle features were used in this build, making it incompatible with Gradle 6.0. Use '--warning-mode all' to show the individual deprecation warnings. See https://docs.gradle.org/5.4.1/userguide/command_line_interface.html#sec:command_line_warnings BUILD SUCCESSFUL in 18s 4 actionable tasks: 4 executed C:¥temp¥brotli-build-script>

Java 9 finalize メソッドが非推奨になったので 新しい JDK でビルドすると警告が出ますが気にしなくても大丈夫です。

ビルドが成功すると build¥libs フォルダーに brotli-1.0.7.jar が出来ます。

これで brotli_jni.dll brotli-1.0.7.jar が揃いました。JAR をビルドするのが面倒という方のために brotli_jni.dll brotli-1.0.7.jar をまとめたアーカイブを置いておきますね。

brotli-1.0.7-java-win-x64.zip (311KB)

使ってみる

DLL JAR が揃ったのでコードを書いていきます。

API の使い方はとても簡単です。org.brotli.wrapper.enc.Encoder クラスに compress スタティックメソッドがあるので 引数に圧縮したバイト列を指定するだけです。

Brotliで文字列を圧縮する
String s = "Hello, World!"; byte[] b = s.getBytes(); byte[] data = org.brotli.wrapper.enc.Encoder.compress(b);

しかし このコードだけでは以下のエラーが出てしまいます。

Exception in thread "main" java.lang.UnsatisfiedLinkError: 
	org.brotli.wrapper.enc.EncoderJNI.nativeCreate([J)Ljava/nio/ByteBuffer;

JNI を使うためにはネイティブライブラリ Windows の場合は DLL をロードしておく必要があるのでした。

Brotli API を呼び出す前にライブラリをロードしておきましょう。main メソッドの先頭やスタティック イニシャライザーでライブラリをロードするのが良いでしょう。

System.load("C:/temp/brotli_jni.dll");

または

System.loadLibrary("brotli_jni");

load メソッドを使用する場合はファイルのフルパスを指定します。loadLibrary メソッドを使用する場合はライブラリのベースファイル名のみを指定します。loadLibrary 使用時はライブラリが PATH 環境変数などで参照可能なパスに配置されている必要があります。

また brotli-1.0.7-java-win-x64.zip アーカイブに含まれている brotli-1.0.7.jar にはライブラリのロードを容易にするための Brotli クラスを追加してあります。このビルド済みアーカイブを使用する場合は以下のコードでネイティブライブラリをロードすることができます。

Brotli.loadLibrary();

この呼び出しは brotli-1.0.7.jar と同じフォルダーにある brotli_jni.dll のフルパスを求めて それを System.load に渡します。brotli_jni.dll PATH 環境変数で参照可能な場所になくてもロードすることができます。

ネイティブライブラリをロードしておけば compress メソッドの呼び出し時に UnsatisfiedLinkError はスローされなくなります。

Encoder.compress が動作したら 次は圧縮データを正しく展開できるかも確認しましょう。Brotli で圧縮したバイト列は``org.brotli.wrapper.dec.Encoder クラスの decompress`スタティックメソッドで展開できます。引数に圧縮データのバイト列を指定すると 展開されたデータが戻り値として返されます。

Brotliで文字列を圧縮する
String s = "Hello, World!"; byte[] b = s.getBytes(); byte[] data = org.brotli.wrapper.enc.Encoder.compress(data); byte[] b2 = Decoder.decompress(data); String s2 = new String(b2); System.out.println(s2);

画面に Hello, World!! と表示されれば成功です。

ファイルサイズを圧縮してサイズを比較する

ファイルも圧縮してみましょう。サンプルデータとして Yahoo!トップページの HTML yahoo-top-page.html というファイル名でローカルに保存しました。これを Brotli で圧縮して yahoo-top-page.html.brotli というファイル名で保存します。

File input  = new File("yahoo-top-page.html");
File output = new File("yahoo-top-page.html.brotli");

try(InputStream in = new FileInputStream(input);
		OutputStream out = new FileOutputStream(output)) {
	out.write(Encoder.compress(in.readAllBytes()));
}

これだけでファイルを Brotli 形式で圧縮できてしまいます。

圧縮前の yahoo-top-page.html 277,181 バイト 圧縮後の yahoo-top-page.html.brotli 73,397 バイトでした。26.48%のサイズになっています。

yahoo-top-page.html Java Zopfli ツオップリ を使う の記事で使用したものと同じファイルです。せっかくなので Zopfli 7-Zip の圧縮結果とも比べてみましょう。

7-Zip では圧縮率の高い 7z 書庫形式と xz 書庫形式を使用しました。それぞれのパラメーターはもっとも圧縮率が高くなるように以下の通り指定しました。

結果は以下のようになりました。

ファイルサイズ圧縮率
未圧縮277,181100.00%
GZIPOutputStream85,28930.77%
jzopfli80,55629.06%
7-Zip 7z 書庫形式75,26227.15%
7-Zip xz 書庫形式75,16827.12%
brotli73,39726.48%

GZIPOutputStream jzopfli よりも圧縮率が高くなるのは当然として 圧縮レベル 超圧縮 を指定した 7-Zip 7z 書庫形式 xz 書庫形式をも凌駕しているのはさすがです。

サンプル プログラム

サンプル プログラムのソースコードは下記のリンクからダウンロードできます。

brotli-sample.zip (456KB)

この記事を共有しませんか?