情報アイランド

「情報を制する者は世界を制す」をモットーに様々な情報を提供することを目指すブログです。現在はプログラミング関連情報が多めですが、投資関連情報も取り扱っていきたいです。

expressモジュールによるHTTPサーバ(23)リクエストボディ(multerモジュール)

この記事は「expressモジュールによるHTTPサーバ」という連載記事の23つ目の記事です。

その他の記事に関しては下を参照してください。

また、HTTP自体について知りたい方は下を参照してください。

この記事ではexpressモジュールとmulterモジュールを使ってメディアタイプ(MIMEタイプ)がmultipart/form-dataのHTTPリクエストボディを処理するHTTPサーバを作成する方法について説明します。

HTTPリクエストボディ

HTTPリクエストボディのデータを取得するには下のようなモジュールを利用する方法があります。

  • body-parser
  • multer

この記事ではmulterモジュールを利用する方法を取り上げます。

ただし、multerモジュールはメディアタイプがmultipart/form-dataのデータにしか対応していませんので注意してください。

メディアタイプがmultipart/form-data以外のデータを取得するにはmulterモジュールの代わりにbody-parserモジュールを利用します

ストレージエンジン

multerモジュールはメディアタイプがmultipart/form-dataのデータの中でfilenameパラメータが付随しているものをメモリ上に保持したり、ファイルとして保存したりする機能を有しますが、データの保持や保存を実際に行うのがストレージエンジンです。

ストレージエンジンには下のような種類があります。

  • MemoryStorage・・・データをメモリ上にバッファとして保持します。
  • DiskStorage・・・データをファイルとして保存します。

MemoryStorage

MemoryStorageストレージエンジンを作成するにはmulter.memoryStorage関数を使用します。

var multer = require('multer');

var memorystorage = multer.memoryStorage();

返り値としてMemoryStorageストレージエンジンが得られます。

DiskStorage

DiskStorageストレージエンジンを作成するにはmulter.diskStorage関数を使用します。

var diskstorage = multer.diskStorage({
    destination: function (req, file, callback) {
        callback(null, 'xxx');
        //or
        callback(new Error('aaa'), null);
    }, 
    filename: function (req, file, callback) {
        callback(null, 'yyy');
        //or
        callback(new Error('bbb'), null);
    }
});

第1引数にオプションをオブジェクトとして指定します。

オプションには下のようなものがあります。

  • destination・・・ファイルを保存するフォルダのパスを指定します。あるいは、ファイルを保存するフォルダのパスを決定する処理を関数として指定することもできます。この関数の第1引数はreqであり、第2引数はデータに関する情報を表すオブジェクトであり、第3引数はコールバック関数です。処理が完了した場合や処理の実行中にエラーが発生した場合には必ずこのコールバック関数を呼び出さなければなりません。このコールバック関数の第1引数にはエラーオブジェクト(エラーが発生しなかった場合にはnull)を指定し、第2引数にはファイルを保存するフォルダのパスを指定します(エラーが発生した場合にはnullを指定します)。デフォルトはos.tmpdir()です。
  • filename・・・ファイル名を決定する処理を関数として指定します。この関数の第1引数はreqであり、第2引数はデータに関する情報を表すオブジェクトであり、第3引数はコールバック関数です。処理が完了した場合や処理の実行中にエラーが発生した場合には必ずこのコールバック関数を呼び出さなければなりません。このコールバック関数の第1引数にはエラーオブジェクト(エラーが発生しなかった場合にはnull)を指定し、第2引数にはファイル名を指定します(エラーが発生した場合にはnullを指定します)。デフォルトはランダムなファイル名です。

データに関する情報を表すオブジェクトは下のようなプロパティを有します。

  • fieldname・・・データに付随しているnameパラメータの値を表します。
  • originalname・・・データに付随しているfilenameパラメータの値を表します。
  • encoding・・・データの文字コードを表します。
  • mimetype・・・データのメディアタイプを表します。
  • size・・・データのサイズをバイト単位で表します。

返り値としてDiskStorageストレージエンジンが得られます。

multer関数

HTTPリクエストボディのデータを取得するミドルウェアを作成するためのオブジェクトを作成するにはmulter関数を使用します。

var m = multer({
    dest: 'xxx', 
    storage: memorystorage, 
    fileFilter: function (req, file, callback) {
        callback(null, true);
        //or
        callback(null, false);
        //or
        callback(new Error('aaa'), null);
    }, 
    limits: {
        fieldNameSize: 100, 
        fieldSize: 1000000, 
        fields: Infinity, 
        fileSize: Infinity, 
        files: Infinity, 
        parts: Infinity, 
        headerPairs: 2000
    }
    preservePath: true
});

第1引数にオプションをオブジェクトとして指定します。

オプションには下のようなものがあります。

  • dest・・・ファイルを保存するフォルダのパスを指定します。
  • storage・・・ストレージエンジンを指定します。
  • fileFilter・・・データを受け入れるかどうかを決定する処理を関数として指定します。この関数の第1引数はreqであり、第2引数はデータに関する情報を表すオブジェクトであり、第3引数はコールバック関数です。処理が完了した場合や処理の実行中にエラーが発生した場合には必ずこのコールバック関数を呼び出さなければなりません。このコールバック関数の第1引数にはエラーオブジェクト(エラーが発生しなかった場合にはnull)を指定し、第2引数にはデータを受け入れるかを真偽値として指定します(エラーが発生した場合にはnullを指定します)。
  • limits・・・データに関する制限を表すオブジェクトを指定します。
  • preservePath・・・データに関する情報を表すオブジェクトのoriginalnameプロパティにfilenameパラメータの値全体を格納するかfilenameパラメータの値が表すパスのベース部分のみを格納するかを指定します。値全体を格納するにはtrueを指定し、ベース部分のみを格納するにはfalseを指定します。

データに関する制限を表すオブジェクトには下のようなプロパティを指定することができます。

  • fieldNameSize・・・nameパラメータのサイズの最大値をバイト単位で指定します。デフォルトは100バイトです。
  • fieldSize・・・filenameパラメータが付随していないデータのサイズの最大値をバイト単位で指定します。デフォルトは1メガバイトです。
  • fields・・・filenameパラメータが付随していないデータの最大数を指定します。デフォルトはInfinityです。
  • fileSize・・・filenameパラメータが付随しているデータのサイズの最大値をバイト単位で指定します。デフォルトはInfinityです。
  • files・・・filenameパラメータが付随しているデータの最大数を指定します。デフォルトはInfinityです。
  • parts・・・データの最大数を指定します。デフォルトはInfinityです。
  • headerPairs・・・データのヘッダ領域のフィールドの最大数を指定します。デフォルトは2000です。

データに関する情報を表すオブジェクトは上で説明したものと同じです。ただし、storageオプションの値がMemoryStorageストレージエンジンである場合には加えて下のようなプロパティを有します。

  • buffer・・・データを保持するバッファを表します。

また、DiskStorageストレージエンジンである場合には加えて下のようなプロパティを有します。

  • destination・・・ファイルを保存するフォルダのパスを表します。
  • filename・・・ファイル名を表します。
  • path・・・パスを表します。

返り値としてミドルウェアを作成するためのオブジェクトが得られます。以後このオブジェクトをmと表記します。

なお、第1引数のdestオプションもstorageオプションも指定しなかった場合にはデータがファイルとして保存されることはありません。

ミドルウェアの作成

HTTPリクエストボディのデータを取得するミドルウェアを作成するには取得するデータの種類に応じて下のような関数を使用します。

var express = require('express');

var app = express();
app.use(m.none());
app.use(m.any());
app.use(m.single('xxx'));
app.use(m.array('xxx', 10));
app.use(m.fields([{
    name: 'xxx', 
    maxCount: 10
}, {
    name: 'yyy', 
    maxCount: 20
}]));
  • m.none関数・・・filenameパラメータが付随しているデータは一切取得しません。
  • m.any関数・・・filenameパラメータが付随している全てのデータをreq.filesに配列として格納します。
  • m.single関数・・・第1引数にnameパラメータの値を指定します。filenameパラメータが付随しており、第1引数の値のnameパラメータが付随している単一のデータをreq.fileに格納します。
  • m.array関数・・・第1引数にnameパラメータの値を指定し、第2引数にデータの最大数を指定します。ただし、第2引数は指定しなくても構いません。filenameパラメータが付随しており、第1引数の値のnameパラメータが付随している1個以上のデータをreq.filesに配列として格納します。
  • m.fields関数・・・第1引数にnameプロパティとmaxCountプロパティから成るオブジェクトの配列を指定します。nameプロパティの値にはnameパラメータの値を指定し、maxCountプロパティの値にはデータの最大数を指定します。ただし、maxCountプロパティは指定しなくても構いません。filenameパラメータが付随しており、第1引数の配列の何れかの要素のオブジェクトのnameプロパティの値に一致するnameパラメータが付随している1個以上のデータをreq.filesにオブジェクトとして格納します。このオブジェクトのプロパティ名はnameパラメータの値となり、プロパティの値はデータの配列となります。

なお、req.filereq.filesに格納されるデータはデータそのものではなくデータに関する情報を表すオブジェクトとなりますので注意してください。

返り値としてミドルウェアが得られます。

このミドルウェアはfilenameパラメータが付随していない全てのデータをreq.bodyにオブジェクトとして格納します。このオブジェクトのプロパティ名はデータのnameパラメータの値となり、プロパティの値はデータそのものとなります。

サンプルコード1

クライアントからPOSTリクエストを受け取ったらメディアタイプがmultipart/form-dataのボディのデータを処理し、全てのデータの内容を標準出力に出力し、JSONデータを返すHTTPサーバを作成し、起動します。

Enterキーが押されたらサーバを停止し、プログラムを終了します。

express-server-multer.js

var express = require('express');
var multer  = require('multer');
var http = require('http');

var app = express();
app.set('env', 'development');
app.set('x-powered-by', false);
app.set('case sensitive routing', true);
app.set('strict routing', true);
app.use(function (req, res, next) {
    res.type('text/plain; charset=utf-8');
    next();
});
app.post('/multipart', multer({ dest: 'upload' }).any(), function (req, res, next) {
    if (!req.body) {
        res.status(400);
        res.send(http.STATUS_CODES[400] + '\r\n');
    }
    else {
        console.log(req.body);
        console.log(req.files);
        res.status(200);
        res.json(req.body);
    }
});
app.post('*', function (req, res, next) {
    res.status(404);
    res.type('text/plain; charset=utf-8');
    res.send(http.STATUS_CODES[404] + '\r\n');
});
app.all('*', function (req, res) {
    res.status(501);
    res.type('text/plain; charset=utf-8');
    res.send(http.STATUS_CODES[501] + '\r\n');
});
var server = app.listen(3000, function () {
    console.log('http server is running...press enter key to exit.');

    process.stdin.on('data', function (data) {
        if (data.indexOf('\n') !== -1) {
            server.close(function () {
                console.log('http server closed.');
                process.exit(0);
            });
        }
    });
});
server.on('error', function (err) {
    console.error(err);
    process.exit(1);
});

使用パッケージ

  • express
    npm install expressでインストールします。
  • Multer
    npm install multerでインストールします。

実行結果

実行結果の確認のためにAdvanced REST clientを使用しています。

コードを実行するとHTTPサーバが起動します。

C:\work\node>node express-server-multer.js
http server is running...press enter key to exit.

Advanced REST clientを使ってサーバに対してHTTPリクエストを行います。

サーバから正しくレスポンスが返っていることが分かります。

また、HTTPリクエストボディのデータが正しく取得できていることが分かります。

C:\work\node>node express-server-multer.js
http server is running...press enter key to exit.
{ name: 'pizyumi', age: '27' }
[ { fieldname: 'age',
    originalname: 'foo.txt',
    encoding: '7bit',
    mimetype: 'text/plain',
    destination: 'upload',
    filename: 'e95ef2184b26aa96ee2a87cb839844dd',
    path: 'upload\\e95ef2184b26aa96ee2a87cb839844dd',
    size: 15 } ]

また、現在のフォルダのuploadフォルダにe95ef2184b26aa96ee2a87cb839844ddという名称のファイルが作成されており、データが格納されていることが分かります。

C:\work\node>dir upload
 Volume in drive C is TI31035600A
 Volume Serial Number is 1CBD-3C48

 Directory of C:\work\node\upload

2017/04/12  00:50    <DIR>          .
2017/04/12  00:50    <DIR>          ..
2017/04/12  00:50                15 e95ef2184b26aa96ee2a87cb839844dd
               1 File(s)             15 bytes
               2 Dir(s)  18,143,084,544 bytes free

C:\work\node>type upload\e95ef2184b26aa96ee2a87cb839844dd
this is a file.

Enterキーを押すとサーバが停止し、プログラムが終了します。

関連

pizyumi
プログラミング歴19年のベテランプログラマー。業務システム全般何でも作れます。現在はWeb系の技術を勉強中。
スポンサーリンク

-express, Node.js