はじめに
ファイルパス(もしくはそれに準ずる文字列)をURLパラメータに使いたいと思いましたが、そのままではエスケープをあれこれしないといけないし、マルチバイトが入ってくると長くなります。そこで文字列をバイナリー圧縮してBase64変換したら良いかな?と思ってやってみました。
開発環境
Java 11
何が嬉しいの?
- 文字列にURLに使用不可な文字があっても使用できる*1
- 文字列の難読化(暗号化ではない)ができる
変換処理
処理の流れは以下の通り
- 文字列をバイナリー圧縮する
- バイナリーをBase64に変換する
- 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; } } }
参考情報
さいごに
短い文字列だと変換後の文字列の方が長くなってしまうことがあります*2。
本来の目的が「ファイルパスのような文字列をURLのパラメータとして使いたいため」なので、それは仕方ないかなぁと。
難読化はしていますが暗号化はしていないので、ご利用時には注意ください。