はじめに
今回から2回に分けてファイルをダウンロードする簡単な Web API と Web UI のサンプルを紹介します。
第1回目の今回は Node.js + Express.js での Web API です。
第2回目は Vue.js + Vuetify で簡単な Web UI を作り、今回の Web API を叩いて実際にファイルをダウンロードします。
- Web API 編(←イマココ)
- Web UI 編
環境
OS: macOS Mojave 10.14.6
Node.js: 10.16.3
Express.js: 4.17.1
API 仕様(概略)
- GET
- URI
/api/download
- クエリパラメータ
file=ファイル名
ファイル名はsample.png
orsample.csv
が指定可能
ディレクトリ構成
root/
┣ api/
┃ ┗ download.js
┣ static/
┃ ┣ sample.csv
┃ ┗ sample.png
┗ app.js
download.js
'use strict';
const express = require('express');
const router = express.Router();
const { check, validationResult } = require('express-validator');
const path = require('path');
const fs = require('fs').promises;
const ContentType = [
{
ext: 'csv',
value: 'text/csv'
},
{
ext: 'png',
value: 'image/png'
},
]
router.get('/', validateParam(), async (req, res) => {
const errors = validationResult(req);
if(!errors.isEmpty()) {
res.status(400).end();
return;
}
const filepath = path.join('static', req.query.file);
const file = await fs.readFile(filepath)
.catch((error) => {
console.log(error);
res.status(404).end();
return;
});
const filenames = req.query.file.split('.');
const contentTypes = getContentType(filenames[filenames.length - 1]);
const headers = {
'Access-Control-Allow-Origin': req.headers.origin,
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Expose-Headers': 'Content-Disposition',
'Access-Control-Allow-Methods': 'GET',
'Content-Type': (contentTypes.length > 0) ? contentTypes[0].value : null,
'Content-Disposition': 'attachment; filename=' + req.query.file
};
res.set(headers).send(file);
});
function validateParam() {
return [
check('file')
.exists({checkNull: true})
];
}
function getContentType(ext) {
return ContentType.filter((contentType) => {
if (ext === contentType.ext) return contentType
})
}
module.exports = router;
ファイル読込
const path = require('path');
const fs = require('fs').promises;
~中略~
const filepath = path.join('static', req.query.file);
const file = await fs.readFile(filepath)
.catch((error) => {
console.log(error);
res.status(404).end();
return;
});
解説
fs.readFile()
で static 配下からファイルを読み込んでいます。
ファイルが無い場合は 404 を返却して終了しています。
ヘッダー設定
const ContentType = [
{
ext: 'csv',
value: 'text/csv'
},
{
ext: 'png',
value: 'image/png'
},
]
~中略~
const filenames = req.query.file.split('.');
const contentTypes = getContentType(filenames[filenames.length - 1]);
const headers = {
'Access-Control-Allow-Origin': req.headers.origin,
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Expose-Headers': 'Content-Disposition',
'Access-Control-Allow-Methods': 'GET',
'Content-Type': (contentTypes.length > 0) ? contentTypes[0].value : null,
'Content-Disposition': 'attachment; filename=' + req.query.file
};
~中略~
function getContentType(ext) {
return ContentType.filter((contentType) => {
if (ext === contentType.ext) return contentType
})
}
解説
ヘッダーにファイルダウンロードをさせるのに必要な設定をしていきます。
まず、 Content-Disposition に attachment; filename=xxx
の形式で指定します。
今回はファイル形式がわかっているので、 Content-Type も指定します(決め打ちでも良かったんですがそれだとイマイチなので、適当に関数を作りましたが、結果イマイチでしたね)。
また、Web UI からのファイルダウンロードを想定するので、 Access-Control-*
を指定して CORS 対策をします。
あと、Web UI 側でダウンロードする際、 Content-Disposition からファイル名を取得したいので、'Access-Control-Expose-Headers': 'Content-Disposition'
を指定します。
動作確認
画質が荒いので(スミマセン)ちょっと見づらいですが、ブラウザからURLを叩いてファイルがダウンロードされているのがわかると思います。
まとめ
今回はファイルをダウンロードする Web API のサンプルを紹介しました。
ファイルの取得方法や、 CORS 対策は実用途によって色々とアレンジが必要になりますが、ファイルダウンロードの基本的な部分は変わりません。
また、 API の引数チェックに express-validator を使用していますが今回は説明を省いています。気になる方はコチラもどうぞ。
次回は Web UI から Web API を実行し、ファイルをダウンロードします。
今回、使用したコードはGitHubで公開しています。
https://github.com/ponko2bunbun/express-file-download-api-sample