"BOKU"のITな日常

BOKUが勉強したり、考えたことを頭の整理を兼ねてまとめてます。

画像などをBase64データにしてURLで受け渡す場合の注意点と対策例など

f:id:arakan_no_boku:20190909212423j:plain

目次

画像などをBase64データにしてURLで受け渡す場合の注意点と対策例

BASE64は、バイナリデータを一定の規則に基づいてテキスト(文字)データに置き換える変換方式の一つです。

アルファベットの大文字(26文字)と小文字(26文字)、数字(10文字)、「+」「/」の2つの記号の64種類の英数字のみを用いてデータを表現します。

テキストしか渡せないURLパラメータなどによく使われますが、JavaScriptBase64エンコードした画像データをWeb-APIにURLパラメータで渡すような場合に、ちょっと落とし穴というか注意すべき点があります。

 

Base64テキストがそのままURLパラメータにできない理由

Base64エンコードデータはテキストです。

だから、URLパラメータにそのまま渡せるような気がします。

でも・・うまくいきません。

なぜかというと、URL内で特別な意味を持つ、「+」 と「 /」 がBase64テキストに含まれるからです。

特に「+」が問題です。

そのままURLのパラメータに渡すと、勝手に「+」が空白に置き換えられます。

なので、サーバー側でBase64からデコードすると、「異常な画像データで例外が発生」してしまいます。

ユーザサイドからみると「500 Internal Server Error」なんかになるわけです。

 

Base64テキストをURLパラメータに渡せるようにするサンプル

Base64エンコードテキストに含まれる「/」や「+」は悪さをしない別の文字に置き換えて、URLパラメータに渡し、受取側(サーバー側)で元に戻してやる処理が必要です。

たとえば、「/」を「!」に、「+」を「-」に置き換えるとかです。

この置換文字のヒントは、Wikipediaからもらいました。

ja.wikipedia.org

Pythonで、GETで受け取ったBase64フォーマットテキスト「img_quoted」に含まれる「/」や「+」を、悪さをしない別の文字に置き換える処理を書くと、こんな感じです。

img_quoted = request.GET.get(key="img", default="")
encode_dic = {'!': '/', '-': '+'}
encode_table = str.maketrans(encode_dic)
image_unquoted = img_quoted.translate(encode_table) 

str.maketransの使い方については、こちらにまとめてます。

arakan-pgm-ai.hatenablog.com

同じようなことを、JavaScript側で書くとすると

var translated = this.images_decodable[0].replace(/\//g, '!').replace(/\+/g, '-') 

みたいな感じですかね。

 

JavaScriptで画像をBase64変換したものも注意が必要

画像ファイルをBase64エンコードしてWebAPIに渡すケースがあります。

そのようなケースで、JavaScriptBase64エンコードする場合の注意点です。

JavaScriptで画像ファイルを読んで、Base64エンコードテキストにしたものは、そのままでは、Python側でデコードできないデータになっていることがあります。

どういうことかというと。

ドロップされた画像ファイルを「readAsDataURL(file)」とかで読んで、reader.resultでBase64エンコードした結果を受け取る・・というのが、JavaScript側でBase64エンコードする「よくある方法」なのですが。

arakan-pgm-ai.hatenablog.com

この時、reader.resultで受け取れるBase64フォーマットには、<img>タグで表示するために必要な「data:image/jpeg;base64,」とか「data:image/png;base64,」がくっついているからなのです。

HTML表示には、とても便利なのですが、そのままでは100%デコードに失敗します。

なので。

サーバー側でデコードさせるためには、事前にそれを削除しておく必要があるというわけです。

JavaScriptで簡単に書くなら

if (fileList[i].type === 'image/jpeg') {
       this.images_decodable.push(String(res).replace('data:image/jpeg;base64,', ''))
} else {
      this.images_decodable.push(String(res).replace('data:image/png;base64,', ''))
}

 みたいに、ファイルのタイプで判断して、頭の余分な文字列を''に置き換えるか感じになるかと思います。

今回はこんなところで。

ではでは。