XMLHttpRequestよりパワフルでフレキシブルと噂のfetch APIだが、文字コードに関して詰まった。Shift_JISでエンコーディングされたテキストをfetchして文字列を取り出そうとしたら、日本語で書かれた部分が文字化けしやがる。UTF8なファイルなら問題ないのに。つらい。

© frontriver – “Fight Mojibake!!!” – Creative Commons: Attribution

解決策がわかんなくて、結局xhrに戻ることにした。xhrはブラウザがよしなにテキストを扱うので、エンコーディングなどという細かいこと気にしなくてすむ。

Body.text()つらい

const text = 'data:text/plain;charset=Shift_JIS;base64,grGC6oLNU2hpZnRfSklTgsWDR4OTg1KBW4NogrOC6oK9g2WDTINYg2eCxYK3';

fetch(text)
.then(res => res.text())
.then(txt => console.log('fetch',txt));
//fetch �����Shift_JIS�ŃG���R�[�h���ꂽ�e�L�X�g�ł�

new Promise(resolve => {
  const xhr = new XMLHttpRequest();
  
  xhr.open('GET', text, true);
  xhr.onload = () => resolve(xhr.response);
  xhr.send();
}).then(txt => console.log("xhr", txt));
//xhr これはShift_JISでエンコードされたテキストです 

上記はfetchとXMLHttpRequestの両方でDATA URIからテキストをconsoleに出力するだけのものだが、fetchの方は見事に文字化けする。(デモはこちら<jsbin>)

これはテキストがShift_JISでエンコーディングされているためで、UTF8でエンコーディングされたテキストだとfetchでも日本語が扱えるようだ。

const utf8text = 'data:text/plain;base64,VVRGOOOBquaWh+Wtl+WIlw=='; //UTF8な文字列

fetch(utf8text)
.then(resp => resp.text())
.then(txt => console.log(txt)); //UTF8な文字列

さて、MDNによればResponseインターフェースが実装するBodyのtextメソッドは「USVString で解決する promise」を返すとあるが、USVstringってなんだ?もっと直観的にファイルをテキストとして取得する方法はないのか???

UTF8しか存在しないユートピアな世界の外でも扱うにはどうしたらいいのか。多分なにかしらの方法はあるんだろうけど、MDNを眺めても解法が見つからなかった。文字コードを変換するという手もあるけど、そこまでするなら初めからXHRを使うほうがよっぽど幸せなのは間違いない。

もしかしたら僕はfetch APIを誤解していたのかもしれない。XMLHttpRequestを代替するもので、jQuery.ajax()的な便利でかゆいところに手が届くアレだと思い込んでいたけど、冷静になって考えるとそこまで便利でもないような気がする。そもそもfetchをキャンセルすることすら現在では出来ないらしいし。fetchはservice workerの中でしか生きていけないのかもしれない。