読者です 読者をやめる 読者になる 読者になる

Make Local Happiness

自分の幸せは自分で作る!!!

xmlHttpRequestでcsvファイルダウンロードしようとしたらちょっとはまった

JavaScript

APIから動的にcsvファイルを保存しようとした際に、
JavaScriptxmlHttpRequestを使用したらちょっとはまったので、メモ。

APIからcsvをダウンロードできるかと思ったのですが、
これができない。

データきてるけど、保存するダイアログがでないという問題があった。

結果から言えば、
xmlHttpRequestは非同期で処理されているため、
ブラウザがファイル保存しようと思ってないみたい。※詳しくは知らない。

意外とAPIで動的に生成するCSVファイルをjavascript側で保存するパターンが見つからなかった。
リンクとかformでリクエストを送っていればこんな問題も起きないので、
あまりやらないパターンなのだと思う。

今回は汎用的にAPIを呼ぶメソッドを用意しておいているので、
APIから情報を取得するのはここにまとめたかった。

ので、(ミカサ風)

どうしてもxmlHttpRequestを使って、csvファイルをダウンロードしたかった。

実装としてはxmlHttpRequestでデータを取得してから、
location.hrefにデータを渡して、呼ぶことで取得したcsvファイルを保存できるようにすることができました。

ソースコード

javascript(クライアント側)

ApiMapper = function(){};
ApiMapper.prototype.apiEndpoint = "http://localhost/api";
ApiMapper.prototype.accessApi = function(method, uri, param, callback_success, callback_failure,callback_status) {
    var xhr = new XMLHttpRequest();
    xhr.timeout = 30000;
    xhr.onsuccess = callback_success;
    xhr.onerror = callback_failure;
    xhr.onsendstream = callback_status;
    xhr.open(method, uri);
    if(method=="POST"){
        xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
    }
    xhr.send(param);
    xhr.onreadystatechange = function (r) {
        if (xhr.readyState == 4) {
          if (xhr.status == 200 || xhr.status == 201) {
            xhr.onsuccess();
          } else {
            xhr.onerror();
          }
        }
    };
    return true;
};

ApiMapper.prototype.getCsvApi = function (param,callback_success, callback_failure){
  return this.accessApi('POST', this.apiEndpoint + "/getcsv.php", "param="+param, callback_success, callback_failure,'');
}

var apiMapper = new ApiMapper();
function outPutCsv(){
    apiMapper.getCsvApi(
      param
      ,function (e) {
        // body...
        console.log("成功しました");
        //ここがキモ
        //this.responseTextデータをlocation.herfにデータとして渡している
        var href = "data:application/octet-stream," + encodeURIComponent(this.responseText);
        location.href = href;
        return true;
      },function (e) {
        // body...
        console.log("失敗しました。");
      }
    );
  }
}

PHP(サーバ側)

<?php
    $username = XXXXXXXXXX; 
    $password = XXXXXXXXXX;
    $host = localhost;
    $database= XXXXXXXXXX;
    $port=3036;
    $table= XXXXXXXXXX;

    $server = new mysqli($host, $username, $password, $database, $port);
    $server->query('SET NAMES utf8');
    $param = $_GET["param"];

    $query = "SELECT * FROM {$table} where `param` = '{$param}'";
    $result = $server->query($query);
    
    $csv = '';
    for ($x = 0; $x < mysqli_num_rows($result); $x++) {
        $tmp = mysqli_fetch_assoc($result);
        foreach ($tmp as $key => $value) {
        //カンマ対応
            $value = str_replace(',', '","', $value);
            //改行対応
            $value = str_replace("\n", chr(10), $value);
            $csv .=  $value . ',';
        }
        $csv .=  "\n"; 
    }
        
    //$filenameにファイル名を指定
    header("Content-Type:application/octet-stream");
    header("Content-Disposition: attachment; filename=ryoc_psi.csv");
    //Excelで開くようにSJISにする
    echo mb_convert_encoding($csv,"SJIS", "UTF-8");
    mysqli_close($server);
?>

おわり。