expressでバイナリデータをデータベースに保存する

諸事情でデータベースに直接バイナリデータを保存する必要が出たのでexpessとsequelizeを使ってやってみました。

スキーマ情報

dataテーブルにfilenameとdataというカラムを作ります。 dataカラムはLONGBLOBを設定しました。

Viewの作成

まずはフォームを作ります。 views/data/new.pug

form(method="POST" enctype="multipart/form-data" action=".")
  input(name="filedata" type="file")
  input(type="text")
  button(type="submit" value="save") 送信
Upload form

Viewのルーティング

router.get('/data/new', function(req, res, next) {
  res.render('data/new');
});

アップロード

受け取ったデータをそのままDBに入れたかったのですが、multipartだと良いライブラリが見つからず、 connect-multipartyを使って一度ファイルに落としたあとfsで読み込んでいます。

const multipart = require('connect-multiparty');
const multipartMiddleware = multipart();

router.post('/data', multipartMiddleware, function(req, res, next) {
  const filedata = req.files.filedata;
  const data = fs.readFileSync(filedata.path);

  db.Data.create({
    filename: filedata.originalFilename,
    data: data
  }).then(()=>{
    req.flash('info', 'uploaded');
    res.set('Location', '/data');
    res.send(301);
  }).catch((error)=>{
    req.flash('info', 'upload error: ' + error);
    res.set('Location', '/data');
    res.send(301);
  });
});

ダウンロード

Content-Dispositionヘッダーでファイル名を指定してダウンロード時に指定のファイル名になるようにしています。

router.get('/data/:id', function(req, res){
  db.Data.findOne({
    where: {id: req.params.id}
  }, {raw: true}).then((data)=>{
    res.set({'Content-Disposition': `attachment; filename="${data.filename}"`});
    res.send(data.data);
  });
});