Javascript で XMLHttpRequest と Blob を用いて、バイナリーデータとしてファイルをサーバサイドで取得し、フロント側でファイルをダウンロードする方法をまとめます。
これまで色々と開発はやってきましたが、チームでやることが多かったためか、私自身が開発も設計もしたことのなかった技術なので調べながらやってみました。
どのような仕様で作ったのかを説明したのちに、実際のコードを用いて説明します。
コード内に理解できるようにコメントと改行を多めに記載しています。
仕様
- 画面でCSVダウンロードボタン押下
- GET通信でJavascirptからサーバサイドに処理をなげる
- サーバサイドでCSVファイルを取得、または作成
- 作成されたCSVをバイナリーデータとしてサーバサイドからJavascirptに返す
- JavascirptではCSV作成が成功していたらダウンロードさせ、失敗していたらアラートだす
この記事の内容は、Javascript処理の2と5になります。
ちなみにサーバサイド側はJavaによりCSV作成をおこないフロントエンドにバイナリファイルとして返却しています。
Javascript
コードと説明と関連技術、そして参考サイトを記載します。
コード
意図がわかるようにコードにコメント形式で記載した内容を載せます。
見やすいよう、改行も多めにしています。コピペで使う際は、不要なものを削除してください。
.on('click', '.csvBtn', function() {
// 異常エラーの際にポップアップでエラーを表示するため
// バイナリ―データとして受け取りダウンロードさせる
var xhr = new XMLHttpRequest();
xhr.open('GET', '/url/getCsv', true);
xhr.responseType = 'arraybuffer';
// イベントハンドラーとして登録
xhr.onload = function(e) {
// onload 時の処理が動いたら成功かチェックさせる
if (this.status == 200) {
var fileName = 'fileName.csv';
// CSVファイル名をバイナリーのヘッダーから取得
// Chromeなどの F12 の DevTools で内容を確認すれば何が来るかわかる
var disposition = xhr.getResponseHeader('Content-Disposition');
if (disposition && disposition.indexOf('attachment') !== -1) {
// 正規表現
var filenameRegex = /filename[^;=\n]=((['"]).*?\2|[^;\n]*)/;
var matches = filenameRegex.exec(disposition);
if (matches != null && matches[1]) {
// matches[1]でとれる⇒ filename*=utf-8''201911%E3%83%87%E3%83%BC%E3%82%BF.csv;
// 不要文字列を消して、デコードしてサーバからのファイル名を取得
fileName = decodeURI(matches[1].replace(/['"]/g, '').replace('utf-8',''));
}
}
var blob = this.response;
// IEとその他で処理の切り分け
if (navigator.appVersion.toString().indexOf('.NET') > 0) {
// IE 10+
// IEだけはこれじゃないとダウンロードできない
window.navigator.msSaveBlob(new Blob([blob]), filename);
} else {
// aタグの生成
var a = document.createElement('a');
// レスポンスからBlobオブジェクト&URLの生成
var blobUrl = window.URL.createObjectURL(new Blob([blob], {
type: blob.type
}));
document.body.appendChild(a);
a.style = 'display: none';
// 生成したURLをセット
a.href = blobUrl;
// ダウンロードの時にファイル名として表示される
a.download = filename;
// クリックイベント発火
a.click();
}
return;
} else {
// エラーごとに分けたい場合は、status で切り分ける
alert("システムエラーが発生いたしました。");
return;
}
};
// リクエストを送信
xhr.send();
})
CSVボタンがクリックされたら、動く処理を記載しています。
XMLHttpRequest を用いてサーバーとの対話処理を記載し、準備ができたら send でサーバーに処理を投げます。
サーバー側の処理が終わった際に動く、Onload処理を準備しておいたので、サーバーの処理が終わったら xhr.onload = function(e) { } 内の処理が始まります。
その中では、XMLHttpRequest の対話で帰ってきたレスポンス情報から、xhr.getResponseHeader(‘Content-Disposition’) によってサーバー側で設定した日本語ファイル名を取得し、英数字から日本語に戻しファイル名とします。
ファイル名を取得したら、ファイルのダウンロードを定義していきます。
this.response には、サーバー側で設定したバイナリーファイルが入っているので、Blob を用いてバイナリーファイルを取得します。
IEだけは、ダウンロード方法を特別用意しないといけません。
IE以外は、a タグとしてリンク化してクリック処理によるダウンロードを行うようにします。
これで完了です。
理解できれば記載がちょっと面倒ではありますが、難しいことではないです。
XMLHttpRequest や Blob に関しては、以下の情報にまとまれています。
私が書くよりリンク先のほうが情報がキレイにまとまっているのでリンクとしました。
関連技術XMLHttpRequest について
XMLHttpRequest (XHR) オブジェクトを使用すると、サーバーと対話することができます。ページ全体を更新する必要なしに、データを受け取ることができます。これでユーザーの作業を中断させることなく、ウェブページの一部を更新することができます。 XMLHttpRequest は AJAX プログラミングで頻繁に使用されます。
詳しくは以下に
外部リンク:開発者向けのウェブ技術>WebAPI>XMLHttpRequest
関連技術Blobについて
バイナリ・ラージ・オブジェクト(英: Binary Large Object、別名:BLOB)
Blob オブジェクトはファイルに似たオブジェクトで、immutable な生データです。データを表す blob は必ずしも JavaScript ネイティブなフォーマットではありません。File インターフェイスは Blob を基礎にしており、その機能を継承する一方で、ユーザーのシステム上のファイルをサポートするための機能を拡張しています。
詳しくは以下に
外部リンク:開発者向けのウェブ技術>WebAPI>Blob
★その他参考サイト
外部リンク:Code Adviser『content-dispositionからファイル名を取得する方法』
外部リンク:Qiita『File APIs(Blob, BlobURL, ArrayBuffer, FileReader)』
外部リンク:reffect『これでXMLHttpRequestが理解できる』
読んで頂き、ありがとうございます。
この記事が誰かにとって、一つの参考となれば幸いです。
新たな知識や技術を習得し続けていきたいです。
コメント
正規表現に * が抜けています。
誤) /filename[^;=\n]=(([‘”]).*?\2|[^;\n]*)/
正) /filename[^;=\n]*=(([‘”]).*?\2|[^;\n]*)/
かなり助かりました。ありがとうございます。