前提
サーバーサイドはLaravel、フロントはVue.jsです。
今回はJavaScriptの話がメインなのでPHPやLaravelが分からない方でも支障なく読んでいただけると思います。
やりたかったこと
ユーザー目線:ブラウザに表示されているダウンロードボタンをクリックすると、CSVファイルをダウンロードできる。
僕目線:LaravelでCSVを吐き出すAPIを用意し、axiosを使ってAPIを叩きCSVをダウンロードできるようにする。
#起こった問題
ブラウザの検索タブでエンドポイントに直接アクセスするとうまくダウンロードできるが、axiosを使ってエンドポイントにアクセスするとダウンロードできない。(CSVの内容はデータとして返ってくるが、ブラウザがダウンロード画面に移行しない。)
失敗した方法
CSVを返すAPIを単純に叩いてダウンロードできると思ったのですが、うまくいきませんでした。
※関係ない部分は色々省略してます。
downloadCsv() {
axios
.get("/api/download/csv", {
})
.catch((error) => {
console.log(error.messagae);
});
},
CSVのデータはちゃんと返ってきますが、ブラウザがダウンロードに移行しません。(ユーザー目線だと何も起こらない。)
うまくいった方法
コードを下記のようにかえるとうまくダウンロードできました。
import saveAs from "file-saver";
// 省略
downloadCsv() {
axios
.get("/api/download/csv", {
responseType: "blob",
})
.then((res) => {
let mineType = res.headers["content-type"];
const name = res.headers["content-disposition"];
const blob = new Blob([res.data], { type: mineType });
saveAs(blob, name);
})
.catch((error) => {
console.log(error.messagae);
});
};
追記部分を解説していきます。
リクエスト時の処理
.get("/api/download/csv", {
responseType: "blob",
})
データをBLOBとして受け取ります。
BLOBとはBinary Large OBject
の略で、IT用語辞典では以下のように解説されています。
BLOBとは、データベースのフィールド定義などで用いられるデータ型の一つで、テキスト(文字列)や整数のように既存のデータ型としては用意されていない任意のバイナリデータを格納するためのもの。
つまり、BlOBはテキストファイルだけではなく画像やPDFなどいろいろな形式のファイルを扱うことができる訳です。
そしてJavaScriptでは受け取ったデータをBOLBにするとこで、ファイルにすることが可能になります。
CSVはテキストデータなのでBLOBにする必要はないのでは?と思ったのですが、データをファイルにするために必要なようです。
レスポンスを受け取った後の処理
.then((res) => {
let mineType = res.headers["content-type"];
const name = res.headers["content-disposition"];
const blob = new Blob([res.data], { type: mineType });
saveAs(blob, name);
})
res.headers["content-type"]
にはLaravel側で設定した'text/csv'
が格納されています。今回は必ず'text/csv'
が返るようになっていますが、ダウンロード用のメソッドを色々な形式に対応できるようにするためにこのように書く必要がありますね。
res.headers["content-disposition"]
にはLaravel側で設定したファイル名が格納されています。
new Blob([res.data], { type: mineType });
JavaScriptのBlobオブジェクトのインスタンスを生成しています。
BlobオブジェクトはBLOBをJavaScriptで扱うためのオブジェクトです。
第一引数にファイルの内容の配列、第二引数にファイルの種類を指定して使います。
saveAs(blob, "file");
'file-server'
というライブラリを使ってファイルを保存しています。
詳しくは割愛しますが、ブラウザ間で異なるファイル保存の処理を1行で書けます。
文字化けを修正
無事CSVをダウンロードすることができましたが、ファイルの内容が文字化けしていました。
はっきりとした原因は分かりませんでしたが、Laravel側でSJIS
形式に変換していた部分をUTF-8
に変換するように変更すると文字化けしなくなりました。
// mb_convert_encoding($content, 'SJIS', 'auto');
mb_convert_encoding($content, 'UTF-8', 'auto');
HTMLのmetaタグでUTF-8に指定しているのが原因なのか?
とりあえず、これで内容も分かるCSVファイルをダウンロードできるようになりました。めでたしめでたし。
最後に
aixosを使ってファイルをダウンロードするにはひと手間いることが分かりました。
BLOBについては恥ずかしながら全然知らなかったです。
今回はLaravel側でCSVを作成しましたが、データをJSON形式で送るようにしてVue側でCSVを作成することもできそうです。
他にもこんな方法があるとか、ここ間違ってるよとかあればご指摘等よろしくお願いします!
ではでは。