1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

第1回 - Web API と Web UI でファイルダウンロード(Web API 編)

Last updated at Posted at 2019-11-17

はじめに

今回から2回に分けてファイルをダウンロードする簡単な Web API と Web UI のサンプルを紹介します。
第1回目の今回は Node.js + Express.js での Web API です。
第2回目は Vue.js + Vuetify で簡単な Web UI を作り、今回の Web API を叩いて実際にファイルをダウンロードします。

  1. Web API 編(←イマココ)
  2. 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 or sample.csv が指定可能

ディレクトリ構成

root/
 ┣ api/
 ┃ ┗ download.js
 ┣ static/
 ┃ ┣ sample.csv
 ┃ ┗ sample.png
 ┗ app.js

download.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;

ファイル読込

download.js(抜粋)
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 を返却して終了しています。

ヘッダー設定

download.js(抜粋)
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-Dispositionattachment; filename=xxx の形式で指定します。
今回はファイル形式がわかっているので、 Content-Type も指定します(決め打ちでも良かったんですがそれだとイマイチなので、適当に関数を作りましたが、結果イマイチでしたね)。
また、Web UI からのファイルダウンロードを想定するので、 Access-Control-* を指定して CORS 対策をします。
あと、Web UI 側でダウンロードする際、 Content-Disposition からファイル名を取得したいので、'Access-Control-Expose-Headers': 'Content-Disposition' を指定します。

動作確認

download.gif
画質が荒いので(スミマセン)ちょっと見づらいですが、ブラウザからURLを叩いてファイルがダウンロードされているのがわかると思います。

まとめ

今回はファイルをダウンロードする Web API のサンプルを紹介しました。
ファイルの取得方法や、 CORS 対策は実用途によって色々とアレンジが必要になりますが、ファイルダウンロードの基本的な部分は変わりません。
また、 API の引数チェックに express-validator を使用していますが今回は説明を省いています。気になる方はコチラもどうぞ。
次回は Web UI から Web API を実行し、ファイルをダウンロードします。

今回、使用したコードはGitHubで公開しています。
https://github.com/ponko2bunbun/express-file-download-api-sample

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?