20
13

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.

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

Posted at

#はじめに
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 編もあります。

終わり

20
13
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
20
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?