LoginSignup
0
0

More than 5 years have passed since last update.

LoopBackフレームワークでCSV出力API実装

Last updated at Posted at 2017-05-29

モデルにCSV出力のremote methodを付けてみた。

  • Streamな感じに出力したい
  • シフトJISな文字コードで出力したい

という点はおおむね達成できたが、csvと組み合わせるとうまく動作しなかったのが心残りだ。
StreamとシフトJIS変換はiconv-liteさまさま。さらにcsvなモジュールでstreamにpipeでうまく処理できるとパーフェクトなのだが。

common/models/note.js
'use strict';
const iconv = require('iconv-lite');
module.exports = function(Note) {
  // サンプル用にModelのfindの代わり
  function find(filter, cb) {
    cb(null, [
      {id: 1, date: new Date(2017, 4, 26), name: 'ラーメン', price: 600},
      {id: 2, date: new Date(2017, 4, 27), name: 'カツ丼', price: 700},
      {id: 3, date: new Date(2017, 4, 28), name: '唐揚げ定食', price: 800},
    ]);
  }

  Note.csv = function(filter, cb) {
    find(filter, function(err, list) {
      if (err) return cb(err);

      const stream = iconv.encodeStream('Shift_JIS');
      cb(null,
        stream,
        'text/csv; charset=shift_jis',
        'attachment; filename=note.csv'
        );

      for (const item of list) {
        const line = ['id', 'date', 'name', 'price'].map(function(key) {
          return item[key];
        }).join(',') + '\n';
        stream.write(line);
      }
      stream.end();
    });
  };

  Note.remoteMethod('csv', {
    accepts: {arg: 'filter', type: 'string', http: {source: 'query'}},
    returns: [
      {arg: 'body', type: 'file', root: true},
      {arg: 'Content-Type', type: 'string', http: {target: 'header'}},
      {arg: 'Content-Disposition', type: 'string', http: {target: 'header'}},
    ],
  });
};

remote methodをLoopBack API Explorerからたたくと、charset付けていてもutf-8に変換されるのは仕様なのだろうか?
HTMLファイルにform作ってpostしてみると、こちらからはちゃんとシフトJISでダウンロード出来るので良しとする。

日本語のファイル名でダウンロードさせたい場合

「日本語.csv」のようなファイル名は、レスポンスヘッダを少し変えて、次のようにする。

Content-Disposition: attachment; filename*=utf-8''%E6%97%A5%E6%9C%AC%E8%AA%9E.csv

filename=ファイル名はブラウザによって対応方法が異なるため、
"attachment; filename*=utf-8''" + encodeURIComponent('日本語.csv')
と、filename*=utf-8''URLエンコードしたファイル名を付けると良さそうです。

モデルのリレーション先も含める場合

findメソッドで、filter引数のincludeプロパティを使うとリレーションも含めた結果がもらえる。
※ただし、結果のリレーション部分のプロパティはFunctionで、もうひと手間toJSON()をかけてやらないとアクセスできないので注意。
参考:https://loopback.io/doc/en/lb3/Include-filter.html#access-included-objects

より汎用的に

モデルのdefinition.rawPropertiesを参照すると、汎用的な処理にできそうです。
mixinにしてしまえば、他のモデルへのCSV出力API追加もさくっと対応できますね。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0