Express.js ファイルアップロード (multer 編)


はじめに

multer はファイルアップロードでよく使われる Express.js ミドルウェアです。サンプルや関連記事も多くて安定した人気です。

multer ではファイルアップロードに関して次のことが可能です。

  • 単一ファイルのアップロード
  • 単一エレメントからの複数ファイルのアップロード
  • 複数エレメントからの複数ファイルのアップロード

npmjs.com のサンプルや説明は簡単な物しかなかったので、上の3つのケースについて簡単に試してみました。

その前に multer の基本ですが次のようにします。

  • multer モジュールをロードする。
    const multer = require('multer');
  • multer オブジェクトを初期化する。
    const upload = multer({dest:updir}); // updir はアップロード先のフォルダ(フルパス)。省略した場合は、OS の一時フォルダが使用される。
  • リクエストハンドラの第2引数には multer オブジェクトのオプションを設定する。
    router.post('/single', upload.single('file1'), (req, res) => { .. });

multer の初期化

multer を使う場合、アップロード先のフォルダを指定しておく必要があります。ただし、これは省略可能で省略すると OS で決まる一時フォルダが使用されます。

const path = require('path');
const multer = require('multer');
const updir = path.dirname(__dirname).replace(/\\/g, "/") + "/tmp";  // アプリケーションフォルダのサブディレクトリ "./tmp" をアップロード先にしている。
const upload = multer({dest:updir});

単一ファイルのアップロード

フォームに 1 つの input[type="file"] エレメントがあり、multiple 属性がない場合の例です。POST ハンドラの第二引数に upload.single('file1') を指定します。ただし、"file1" は input[type="file"] エレメントの name 属性です。

router.post('/single', upload.single('file1'), (req, res) => {
    const path = req.file.path.replace(/\\/g, "/");
    if (path) {
        const dest = updir + "/" + req.file.originalname;
        fs.renameSync(path, dest);  // 長い一時ファイル名を元のファイル名にリネームする。
        res.render('upload', {message: `${dest} にアップロードされました。`});
    }
    else {
        res.render('upload', {message: "エラー:アップロードできませんでした。"});
    }
});

この時のフォームは次のような感じなります。

<form id="form1" method="POST" enctype="multipart/form-data" action="/upload/single">
    <fieldset class="form-group">
        <label for="file1">アップロードファイル (1)</label>
        <input type="file" id="file1" name="file1" class="form-control" />
    </fieldset>
    <fieldset class="form-group">
        <input type="submit" id="submitButton" name="submitButton" class="btn btn-primary" value=" 送信 " />
    </fieldset>
</form>
<p class="message" id="message"><%= message %></p>

複数ファイルのアップロード

複数ファイルをアップロードする場合は、post ハンドラの第二引数に upload.array('file1', MAXFILES) が必要です。'file1' はフォームの input[type="file"] の name 属性、MAXFILES はアップロードできるファイルの最大数です。upload は multer オブジェクトです。

const MAXFILES = 3;
router.post('/multiple', upload.array('file1', MAXFILES), (req, res) => {
    const n = req.files.length;
    try {
        for (let i = 0; i < n; i++) {
            const path = req.files[i].path.replace(/\\/g, "/");
            const dest = updir + "/" + req.files[i].originalname;
            fs.renameSync(path, dest);  // 長い一時ファイル名を元のファイル名にリネームする。
        }
        res.render('upload', {message: `${n} 個のファイルがアップロードされました。`});
    }
    catch (err) {
        res.render('upload', {message: "エラー:アップロードできませんでした。"});
    }
});

この時のフォームは次のような感じなります。input[type="file"] タグに multiple 属性が付与されています。multiple 属性を設定すると、ファイル選択ダイアログで Ctrl キーを押しながら複数ファイルを選択できます。

<form id="form1" method="POST" enctype="multipart/form-data" action="/upload/multiple">
    <fieldset class="form-group">
        <label for="file1">アップロードファイル (1)</label>
        <input type="file" id="file1" name="file1" class="form-control" multiple />
    </fieldset>
    <fieldset class="form-group">
        <input type="submit" id="submitButton" name="submitButton" class="btn btn-primary" value=" 送信 " />
    </fieldset>
</form>
<p class="message" id="message"><%= message %></p>

複数エレメントからのアップロード

フォームには複数の input[type="file"] がある場合がありますが、そういう場合は post ハンドラの第二引数に upload.fields(namedOption) が必要です。upload は multer オブジェクトです。namedOption は、複数 input[type="file"] の指定とアップロードできるファイルの最大数の一覧です。

const MAXFILES = 3;
const namedOption = [
    {'name':'file1', maxCount:1}, {'name':'file2', maxCount:MAXFILES}
];
router.post('/named', upload.fields(namedOption), (req, res) => {
    // file1
    const path1 = req.files['file1'][0].path.replace(/\\/g, "/");
    if (path1) {
        const dest1 = updir + "/" + req.files['file1'][0].originalname;
        fs.renameSync(path1, dest1);  // 長い一時ファイル名を元のファイル名にリネームする。
    }
    else {
        res.render('upload', {message: "エラー:アップロードできませんでした。(file1)"});
        return;
    }
    // file2
    const n = req.files['file2'].length;
    for (let i = 0; i < n; i++) {
        let path2 = req.files['file2'][i].path.replace(/\\/g, "/");
        let dest2 = updir + "/" + req.files['file2'][i].originalname;
        fs.renameSync(path2, dest2);
    }
    res.render('upload', {message: `${n+1} 個のファイルがアップロードされました。`});
});

この時のフォームは次のような感じなります。input[type="file"] (name="file2") タグに multiple 属性が付与されています。multiple 属性を設定すると、ファイル選択ダイアログで Ctrl キーを押しながら複数ファイルを選択できます。

<form id="form1" method="POST" enctype="multipart/form-data" action="/upload/multiple">
    <fieldset class="form-group">
        <label for="file1">アップロードファイル (1)</label>
        <input type="file" id="file1" name="file1" class="form-control" />
    </fieldset>
    <fieldset class="form-group">
        <label for="file1">アップロードファイル (2)</label>
        <input type="file" id="file2" name="file2" class="form-control" multiple />
    </fieldset>
    <fieldset class="form-group">
        <input type="submit" id="submitButton" name="submitButton" class="btn btn-primary" value=" 送信 " />
    </fieldset>
</form>
<p class="message" id="message"><%= message %></p>

ファイルアップロード express-form-data 編もあります。

終わり