4
Help us understand the problem. What are the problem?

posted at

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 編もあります。

終わり

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
4
Help us understand the problem. What are the problem?