1. 概要

この短いチュートリアルでは、Java8とApacheCommonsCodecを使用してPDFファイルのBase64エンコードとデコードを行う方法を説明します。

ただし、最初に、Base64の基本を簡単に見てみましょう。

2. Base64の基本

有線でデータを送信する場合は、バイナリ形式で送信する必要があります。 ただし、0と1だけを送信すると、トランスポート層プロトコルが異なると解釈が異なり、飛行中にデータが破損する可能性があります。

そのため、バイナリデータを転送する際の移植性と共通の標準を実現するために、Base64が登場しました

送信者と受信者の両方が標準の使用を理解し、同意しているため、データが失われたり誤解されたりする可能性が大幅に減少します。

次に、これをPDFに適用するいくつかの方法を見てみましょう。

3. Java8を使用した変換

Java 8以降、Base64エンコーディングスキーム用のエンコーダーとデコーダーを提供するユーティリティjava.util.Base64があります。 RFC4648およびRFC2045 で指定されているように、基本、URLセーフ、およびMIMEタイプをサポートします。

3.1. エンコーディング

PDFをBase64に変換するには、最初にそれをバイト単位で取得し、java.util.Base64.Encoderのencodeメソッドに渡す必要があります。

byte[] inFileBytes = Files.readAllBytes(Paths.get(IN_FILE)); 
byte[] encoded = java.util.Base64.getEncoder().encode(inFileBytes);

ここで、IN_FILEは入力PDFへのパスです。

3.2. ストリーミングエンコーディング

大きなファイルやメモリが限られているシステムの場合、メモリ内のすべてのデータを読み取る代わりにストリームを使用してエンコードを実行する方がはるかに効率的です。 これを達成する方法を見てみましょう:

try (OutputStream os = java.util.Base64.getEncoder().wrap(new FileOutputStream(OUT_FILE));
  FileInputStream fis = new FileInputStream(IN_FILE)) {
    byte[] bytes = new byte[1024];
    int read;
    while ((read = fis.read(bytes)) > -1) {
        os.write(bytes, 0, read);
    }
}

ここで、 IN_FILE は入力PDFへのパスであり、OUT_FILEはBase64でエンコードされたドキュメントを含むファイルへのパスです。 PDF全体をメモリに読み込んでからドキュメント全体をメモリにエンコードする代わりに、一度に最大1Kbのデータを読み取り、そのデータをエンコーダを介してOutputStreamに渡します。

3.3. デコード

受信側で、エンコードされたファイルを取得します。

したがって、元のバイトを取得するためにデコードし、それらをFileOutputStreamに書き込んで、デコードされたPDFを取得する必要があります。

byte[] decoded = java.util.Base64.getDecoder().decode(encoded);

FileOutputStream fos = new FileOutputStream(OUT_FILE);
fos.write(decoded);
fos.flush();
fos.close();

ここで、 OUT_FILE は、作成するPDFへのパスです。

4. ApacheCommonsを使用した変換

次に、ApacheCommonsCodecパッケージを使用して同じことを実現します。 これはRFC2045 に基づいており、前に説明したJava8の実装よりも前のものです。 そのため、複数のJDKバージョン(レガシーバージョンを含む)またはベンダーをサポートする必要がある場合、これはサードパーティのAPIとして役立ちます。

4.1. Maven

Apacheライブラリを使用できるようにするには、pom.xmlに依存関係を追加する必要があります。

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.14</version>
</dependency>

上記の最新バージョンは、 MavenCentralにあります。

4.2. エンコーディング

手順はJava8の場合と同じですが、今回は元のバイトをorg.apache.commons.codec.binary.Base64encodeBase64メソッドに渡します。クラス:

byte[] inFileBytes = Files.readAllBytes(Paths.get(IN_FILE));
byte[] encoded = org.apache.commons.codec.binary.Base64.encodeBase64(inFileBytes);

4.3. ストリーミングエンコーディング

ストリーミングエンコーディングは、このライブラリではサポートされていません。

4.4. デコード

ここでも、 decodeBase64 メソッドを呼び出して、結果をファイルに書き込みます。

byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(encoded);

FileOutputStream fos = new FileOutputStream(OUT_FILE);
fos.write(decoded);
fos.flush();
fos.close();

5. テスト

次に、簡単なJUnitテストを使用してエンコードとデコードをテストします。

public class EncodeDecodeUnitTest {

    private static final String IN_FILE = // path to file to be encoded from;
    private static final String OUT_FILE = // path to file to be decoded into;
    private static byte[] inFileBytes;

    @BeforeClass
    public static void fileToByteArray() throws IOException {
        inFileBytes = Files.readAllBytes(Paths.get(IN_FILE));
    }

    @Test
    public void givenJavaBase64_whenEncoded_thenDecodedOK() throws IOException {
        byte[] encoded = java.util.Base64.getEncoder().encode(inFileBytes);
        byte[] decoded = java.util.Base64.getDecoder().decode(encoded);
        writeToFile(OUT_FILE, decoded);

        assertNotEquals(encoded.length, decoded.length);
        assertEquals(inFileBytes.length, decoded.length);
        assertArrayEquals(decoded, inFileBytes);
    }

    @Test
    public void givenJavaBase64_whenEncodedStream_thenDecodedStreamOK() throws IOException {
        try (OutputStream os = java.util.Base64.getEncoder().wrap(new FileOutputStream(OUT_FILE));
          FileInputStream fis = new FileInputStream(IN_FILE)) {
            byte[] bytes = new byte[1024];
            int read;
            while ((read = fis.read(bytes)) > -1) {
                os.write(bytes, 0, read);
            }
        }

        byte[] encoded = java.util.Base64.getEncoder().encode(inFileBytes);
        byte[] encodedOnDisk = Files.readAllBytes(Paths.get(OUT_FILE));
        assertArrayEquals(encoded, encodedOnDisk);

        byte[] decoded = java.util.Base64.getDecoder().decode(encoded);
        byte[] decodedOnDisk = java.util.Base64.getDecoder().decode(encodedOnDisk);
        assertArrayEquals(decoded, decodedOnDisk);
    }

    @Test
    public void givenApacheCommons_givenJavaBase64_whenEncoded_thenDecodedOK() throws IOException {
        byte[] encoded = org.apache.commons.codec.binary.Base64.encodeBase64(inFileBytes);
        byte[] decoded = org.apache.commons.codec.binary.Base64.decodeBase64(encoded);

        writeToFile(OUT_FILE, decoded);

        assertNotEquals(encoded.length, decoded.length);
        assertEquals(inFileBytes.length, decoded.length);

        assertArrayEquals(decoded, inFileBytes);
    }

    private void writeToFile(String fileName, byte[] bytes) throws IOException {
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(bytes);
        fos.flush();
        fos.close();
    }
}

ご覧のとおり、最初に @BeforeClass メソッドで入力バイトを読み取り、両方の@Testメソッドで次のことを確認しました。

  • エンコードされたバイト配列とデコードされたバイト配列の長さが異なります
  • inFileBytesおよびデコードされたバイト配列は同じ長さで同じ内容です

もちろん、作成したデコードされたPDFファイルを開いて、内容が入力として指定したファイルと同じであることを確認することもできます。

6. 結論

このクイックチュートリアルでは、JavaのBase64ユーティリティについて詳しく学びました。

また、Java8とApacheCommonsCodecを使用してPDFをBase64との間で変換するためのコードサンプルも確認しました。 興味深いことに、JDKの実装はApacheの実装よりもはるかに高速です。

いつものように、ソースコードはGitHubから入手できます。