JavaScriptでCSVやテキストファイルなどを出力したとき、環境や見るツールによっては文字化けしてしまう。たとえばCSVで出力して、テキストエディタでは読めるけど、Excelで開くと文字化けするなど。

結論からいうと、テキストの先頭にBOMをつければよい。
ということで、各ブラウザ(Chrome, Egde, Firefox, IE11, Safari, Opera...)でファイルを出力するときに文字化けさせない実装方法をまとめてみる。

BOM(Byte Order Mark)とは


Unicodeの符号化形式で符号化したテキストの先頭につける数バイトのデータのことである。このデータを元にUnicodeで符号化されていることおよび符号化の種類の判別に使用する。

バイトオーダーマーク - Wikipedia



 

BOM付きファイルを出力する

var csv = 'ここにCSVのデータを入れる';

var link = document.createElement('a');
var bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
var blob;

if (window.navigator.msSaveOrOpenBlob) {
  // for ie
  blob = new Blob([bom, csv], {type: 'text/csv'});
  window.navigator.msSaveOrOpenBlob(blob, csvName);
} else if (window.webkitURL && window.webkitURL.createObjectURL) {
  // for chrome (and safari)
  blob = new Blob([bom, csv], {type: 'text/csv'});
  link.setAttribute('download', csvName);
  link.setAttribute('href', window.webkitURL.createObjectURL(blob));
  link.click();
} else if (window.URL && window.URL.createObjectURL) {
  // for firefox
  blob = new Blob([bom, csv], {type: 'text/csv'});
  link.setAttribute('download', csvName);
  link.setAttribute('href', window.URL.createObjectURL(blob));
  link.click();
}


new Uint8Array([0xEF, 0xBB, 0xBF]) がUTF-8のBOMになる。
new Blob([bom, csv]) でBOM付きのファイルをつくる。

あとはブラウザによって出力方法を変えている。
ただし、Safariの場合はうまく動作しないので注意!
 

ファイルのアップロード・ダウンロードを実装する

JavaScriptでファイルをアップロード・ダウンロードする実装方法をまとめる。  

ファイルのアップロード・ダウンロードを実装する

<!doctype html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>ファイルのアップロードとダウンロード</title>
</head>
<body>
    <div>
        <button id="upload">upload</button>
        <span id="download"></span>
    </div>
    <div>
        <input type="file" id="upload-file" />
        <textarea id="upload-text" readonly></textarea>
    </div>
    <script src="index.js"></script>
</body>
</html>
var upload = document.getElementById('upload');

// uploadボタンを使わずファイル選択したらすぐuploadしたい場合は、changeを使う
// uploadFile.addEventListener('change', function() {

// uploadボタンをクリックでアップロード処理をし、ダウンロード用リンクを作成する
upload.addEventListener('click', function () {
    var uploadFile = document.getElementById('upload-file');
    var file = uploadFile.files[0];
    if (!file) alert('ファイルを選択してください。');

    /********************************
      * upload処理
      ********************************/
    var uploadData;
    var uploadText = document.getElementById('upload-text');
    var reader = new FileReader();
    
    reader.readAsText(file);
    reader.onload = function () {
        // そのまま表示
        uploadText.innerHTML = reader.result;
        // JSONに変換
        uploadData = JSON.parse(reader.result);
    }

    /********************************
      * download用リンクの作成
      ********************************/
    //reader.onloadが非同期処理なので、1秒遅延させる
    setTimeout(function () {
        var downloadFile = JSON.stringify(uploadData);
        var a = document.createElement('a');
        a.textContent = 'download';
        a.download = 'file.json';
        a.href = URL.createObjectURL(new Blob([downloadFile], {type: 'text.plain'}));
        a.dataset.downloadurl = ['text/plain', a.download, a.href].join(':');

        var downloadLink = document.getElementById('download');
        downloadLink.appendChild(a);
    }, 1000);
});

各ブラウザ(Chrome, Firefox, Edge, IE11, Safari)でダウンロード処理を実装する

Webページから何らかのファイルをダウンロードしたい場合は、ブラウザごとに実装する必要がある。 以下のブラウザを対象とする。

  • Chrome
  • Firefox
  • Edge
  • Internet Explorer 11
  • Safari

ダウンロード処理を実装する

// ダウンロードしたいコンテンツ、MIMEType、ファイル名
var content  = 'abc';
var mimeType = 'text/plain';
var name     = 'test.txt';

// BOMは文字化け対策
var bom  = new Uint8Array([0xEF, 0xBB, 0xBF]);
var blob = new Blob([bom, content], {type : mimeType});

var a = document.createElement('a');
a.download = name;
a.target   = '_blank';

if (window.navigator.msSaveBlob) {
  // for IE
  window.navigator.msSaveBlob(blob, name)
}
else if (window.URL && window.URL.createObjectURL) {
  // for Firefox
  a.href = window.URL.createObjectURL(blob);
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}
else if (window.webkitURL && window.webkitURL.createObject) {
  // for Chrome
  a.href = window.webkitURL.createObjectURL(blob);
  a.click();
}
else {
  // for Safari
  window.open('data:' + mimeType + ';base64,' + window.Base64.encode(content), '_blank');
}

Edge, Internet Explorer 11

window.navigator.msSaveBlob を使う。

Firefox

<a>タグを作成し、そのリンクにwindow.URL.createObjectURLを設定すれば、a.clickでダウンロード処理を実行することができる。ただし、clickイベントを発火する前にbodyなどに追加しなければ正常に動作しないので注意。もちろんclickイベントが終わったらremoveChildで削除する。

Chrome

window.webkitURL.createObjectURLを使う。Firefoxの処理に似ているが違いとしては、Chromeの場合はbodyなどに追加しなくてもメモリ上だけで対応できるという点がある。だからappendChildもremoveChildもする必要がない。