システム開発で思うところ

Javaで主にシステム開発をしながら思うところをツラツラを綴る。主に自分向けのメモ。EE関連の情報が少なく自分自身がそういう情報があったら良いなぁということで他の人の参考になれば幸い

文字列をBase64に変換する

はじめに

ファイルパス(もしくはそれに準ずる文字列)をURLパラメータに使いたいと思いましたが、そのままではエスケープをあれこれしないといけないし、マルチバイトが入ってくると長くなります。そこで文字列をバイナリー圧縮してBase64変換したら良いかな?と思ってやってみました。

開発環境

Java 11

何が嬉しいの?

  • 文字列にURLに使用不可な文字があっても使用できる*1
  • 文字列の難読化(暗号化ではない)ができる

変換処理

処理の流れは以下の通り

  1. 文字列をバイナリー圧縮する
  2. バイナリーをBase64に変換する
  3. URLに使用してはいけない文字列を置換する

デコードは、この逆をします。

実装

import java.util.Arrays;
import java.util.Base64;
import java.util.logging.Logger;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

public class StringCompressor {

    private static final Logger logger = Logger.getLogger(StringCompressorImpl.class.getName());

    public String deflate(String value) {
        byte[] input = value.getBytes(java.nio.charset.StandardCharsets.UTF_8);
        byte[] output = new byte[256];
        Deflater compresser = new Deflater();
        compresser.setInput(input);
        compresser.finish();
        int compressedDataLength = compresser.deflate(output);
        var trimed = Arrays.copyOfRange(output, 0, compressedDataLength);
        var base64 = Base64.getEncoder().encode(trimed);
        var encoded = new String(base64, java.nio.charset.StandardCharsets.UTF_8);

        //base64で使用されていてURLで使用されることが推奨されていない文字を置換
        var replaced = encoded
                .replaceAll("\\+", "_")
                .replaceAll("/", "-")
                .replaceAll("=", "!");

        return replaced;
    }

    public String inflate(String value) {
        try {
            // deflateで置換した文字列をbase64用に置き換える
            var base64 = value
                    .replaceAll("_", "+")
                    .replaceAll("-", "/")
                    .replaceAll("!", "=");

            var decoded = Base64.getDecoder().decode(base64);

            Inflater decompresser = new Inflater();
            decompresser.setInput(decoded);
            byte[] output = new byte[256];
            int resultLength = decompresser.inflate(output);
            decompresser.end();

            var result = new String(output, 0, resultLength, java.nio.charset.StandardCharsets.UTF_8);
            return result;
        } catch (DataFormatException ex) {
            logger.warning(() -> ex.getMessage() + "::String could not decode " + value + "::");
            return value;
        }
    }

}

参考情報

Base64 - Wikipedia

URLに使用できる文字、できない文字 – ysklog

さいごに

短い文字列だと変換後の文字列の方が長くなってしまうことがあります*2
本来の目的が「ファイルパスのような文字列をURLのパラメータとして使いたいため」なので、それは仕方ないかなぁと。
難読化はしていますが暗号化はしていないので、ご利用時には注意ください。

*1:受信後デコードが必要ですが

*2:おそらくほぼ確実に