平文の文字列をデータとして扱う時に、一抹の不安がよぎるのは、エンジニアの宿命です。
Base64や、文字コード化して、難読化処理を施すのは簡単に実現できますが、
インターネット通信を行う場合は、データを圧縮してできるだけパケット通信を抑えて、通信速度を減らしたいと考えるのも、エンジニアならではの義務感です。
そこで今回は、deflateアルゴリズムを用いた、比較的メジャーな圧縮を使って、文字列の
難読化+圧縮処理をツール化してみました。
ツール
ソースコード
index.html
<link rel="stylesheet" href="css/style.css">
<script type="module" src="js/main.js"></script>
<section>
<textarea class="text-archive" name="string"></textarea>
<div class="control">
<button name="encode">文字列を圧縮 ↓</button>
<button name="decode">↑ 圧縮データを復元</button>
</div>
<textarea class="text-archive" name="archive"></textarea>
</section>
css/style.css
textarea.text-archive{
width:500px;
max-width:100%;
min-height:150px;
margin:10px;
border:1px solid #AAA;
border-radius:10px;
padding:10px;
}
.control{
width:500px;
display:flex;
gap:10px;
margin:10px;
align-items:center;
justify-content:center;
}
js/main.js
import { Archive } from "./archive.js"
class Main{
constructor(){
new Archive()
}
}
switch(document.readyState){
case "complete": case "interactive":
new Main()
break
default:
window.addEventListener("DOMContentLoaded" , (()=>new Main()))
}
js/archive.js
export class Archive{
path_rawdeflate = "js/zlib/rawdeflate.min.js"
path_rawinflate = "js/zlib/rawinflate.min.js"
get elm_textarea_string(){
return document.querySelector(`textarea[name="string"]`)
}
get elm_textarea_archive(){
return document.querySelector(`textarea[name="archive"]`)
}
get button_encode(){
return document.querySelector(`button[name="encode"]`)
}
get button_decode(){
return document.querySelector(`button[name="decode"]`)
}
constructor(){
this.put_script(this.path_rawdeflate)
this.put_script(this.path_rawinflate)
this.button_encode.addEventListener("click" , this.encode.bind(this))
this.button_decode.addEventListener("click" , this.decode.bind(this))
}
put_script(path){
const script = document.createElement("script")
script.src = path
document.body.appendChild(script)
}
encode(){
const str = this.elm_textarea_string.value
const val = escape(encodeURIComponent(str))
const arr = new TextEncoder().encode(val)
const deflate = new Zlib.RawDeflate(arr).compress()
const data = ((chars)=>{
let str = ""
for(const char of chars){
str += String.fromCharCode(char)
}
return str
})(deflate)
this.elm_textarea_archive.value = btoa(data)
}
decode(){
const val = atob(this.elm_textarea_archive.value)
const chars = ((val)=>{
const arr = []
for(let i=0; i<val.length; i++){
arr.push(val[i].charCodeAt(0))
}
return new Uint8Array(arr)
})(val)
const inflate = new Zlib.RawInflate(chars).decompress()
const str = new TextDecoder().decode(inflate);
this.elm_textarea_string.value = decodeURIComponent(unescape(str))
}
}
zlibモジュールの配置
zlibライブラリは、以下のgithubから取得しています。
https://github.com/imaya/zlib.js
cloneして使っても良いんですが、必要なのはrawdeflateとrawinflateの2つだけなので、以下のリンクからソースコードをコピペしても大丈夫です。
rawdeflate :
https://github.com/imaya/zlib.js/blob/develop/bin/rawdeflate.min.js
rawinflate :
https://github.com/imaya/zlib.js/blob/develop/bin/rawinflate.min.js
DLした上記ファイルを、js/zlib/以下に配置します。
解説
実は文字列を圧縮する時に、バイナリデータだと、データ量が少なくなるんですが、それをまた文字列にbase64変換しているため、元データよりも大きく膨らんでしまっています。
zipファイルなどでダウンロードする形式にしておけば、軽いデータのやりとりになりますね。
今回、作ってから気がついたので、このまま強引に進めてしまいますwww。
文字エンコードで、new Zlib.RawDeflate(arr).compress()とした直後のデータは、00,00,00みたいな文字コードの配列データのようないわゆるunit8Array形式のデータなので、
これを
fromCharCodeで、文字に戻してあげます。
でもこの文字は、asciiキャラクタにならないものもあるので、base64で文字列変換します。
デコードはこの逆の操作ですね。
あとがき
ハマったのは、
new TextDecoder()を利用したエンコード、デコードで、文字列を前後で処理しないと、思った通りの文字変換が行われないという事がわかりました。
いったい何度エンコードを行っているんでしょう?
きっと、もっとシンプルな変換ができると思うので、今回のバージョンはモックアップとして公開しておきます。
0 件のコメント:
コメントを投稿