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
をまとめたアーカイブを置いておきますね。
使ってみる
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,181 | 100.00% |
GZIPOutputStream | 85,289 | 30.77% |
jzopfli | 80,556 | 29.06% |
7-Zip (7z 書庫形式) | 75,262 | 27.15% |
7-Zip (xz 書庫形式) | 75,168 | 27.12% |
brotli | 73,397 | 26.48% |
GZIPOutputStream、 jzopfli よりも圧縮率が高くなるのは当然として、 圧縮レベル 「超圧縮」 を指定した 7-Zip の 7z 書庫形式、 xz 書庫形式をも凌駕しているのはさすがです。
サンプル・プログラム
サンプル ・ プログラムのソースコードは下記のリンクからダウンロードできます。