|||||||||||||||||||||

なんぶ電子

- 更新: 

JavaScriptのAjaxでバイナリデータをダウンロード

Binay file download with AJAX

POSTでパラメータを受け、文字コードがSJIS-win(CP932)のCSVファイルをダウンロードとして出力するPHPのページを作ったのですが、このページにAJAX経由からアクセスしようとした際に実装に苦労したので方法を書き残しておきます。

手持ちの資源はjQueryだったので、jQueryの$.Ajaxでの方法を考えたのですがそれでは実現できずにFetchを使うことになりました。

Fetch APIは過去にAJAXについての記事でも触れましたが、近年のブラウザなら標準で利用できます。

PHPのコード

PHPのコードでは次のような感じで、エンコードSJIS、改行コードCRLFのCSVファイルを作成しています。

HTMLのformからこのファイル宛にPOSTすればダウンロードが始まるように設定されています。

ちなみに、ここではCSVファイルを生成していますですが、別記事で紹介しているPhpSpreadsheetを使えばエクセルファイルを生成することもできます。

download-page.php

<?php

// POSTされたパラメータを取得
$params = json_decode(file_get_contents("php://input"),true);

// 2次元配列のデータを取得
$data=getData($params);

// CSVデータ作成
// エクセルで表示できるようにエンコーディングをSJISに
// 改行コードをCRLFに
$contents=mb_convert_encoding("CSVデータ","SJIS-win");
$contents.="\r\n";

for ($i = 0; $i < count($data); $i++) {
  for($j =0; $j < count($data[$i]); $j++) {
    if ($j != 0) {
      $contents.=",";
    }
  }
  $contents.=mb_convert_encoding(str_replace(",","",$data[$i]),"SJIS-win");
}
$contents.="\r\n";


// content-type
header('Content-Type: application/octet-stream');

// ブラウザのMIMEタイプを判断を阻止
header('X-Content-Type-Options: nosniff');

// ファイルのサイズ
header('Content-Length: ' . strlen($contents));

// ファイル名はajax経由の場合jsで名付けます
header('Content-Disposition: attachment; filename="'.date('Y-m-d_H-i-s').'.csv"');

// keep aliveを無効に
header('Connection: close');

// 出力
echo  $contents;

exit(0);

AJAXのコード

先のPHPのコードではPOSTされた値を「php://input」で取得するようにしていたので、Content-Typeをapplication/jsonにしています。

JavaScriptのFetch APIのレスポンスは、Promiseで返ります。

Promiseの使い方については過去の記事でも触れましたので合わせて読んでいただけると幸いです。

fetchで得られたレスポンスにはレスポンスの本文をArrayBuffer化するarrayBuffer()メソッドがあり、これを呼ぶとPromiseが連鎖します。

arrayBufferとして得られた値をapplication/octet-streamのBlobでイニシャライズした後、aエレメントにセットしてダウンロードを開始させています。

sample.html


<script>
// AJAXするアドレス
let strAjaxUrl='./download-page.php';
// POSTパラメタ
let params={ key: value };

fetch(strAjaxUrl,{
  method: 'post',
  headers:{
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(params)
}).then(res=>{
  return res.arrayBuffer();
}).then(ab=>{
  let blob = new Blob([ab],{type: 'application/octet-stream'});
  let a = document.createElement('a');
  a.download="ファイル名.csv";
  a.href=window.URL.createObjectURL(blob);
  a.click();
  //ダウンロード後の片付け
  blob=null;
  a.remove();
}).catch((err) => {
  alert(err);
  console.error(err);
}).finally(() => {
  //成功、失敗問わない最終処理
});
</script>
...

筆者紹介


自分の写真
がーふぁ、とか、ふぃんてっく、とか世の中すっかりハイテクになってしまいました。プログラムのコーディングに触れることもある筆者ですが、自分の作業は硯と筆で文字をかいているみたいな古臭いものだと思っています。 今やこんな風にブログを書くことすらAIにとって代わられそうなほど技術は進んでいます。 生活やビジネスでPCを活用しようとするとき、そんな第一線の技術と比べてしまうとやる気が失せてしまいがちですが、おいしいお惣菜をネットで注文できる時代でも、手作りの味はすたれていません。 提示されたもの(アプリ)に自分を合わせるのでなく、自分の活動にあったアプリを作る。それがPC活用の基本なんじゃなかと思います。 そんな意見に同調していただける方向けにLinuxのDebianOSをはじめとした基本無料のアプリの使い方を紹介できたらなと考えています。

広告