Help us understand the problem. What is going on with this article?

Node.js + express + formidable でファイルアップロードを受け付ける

More than 5 years have passed since last update.

備忘録です.
Node.js と express,formidable を使って,POSTでファイルのアップロードを受け付ける処理を書きました.

環境

  • MacOSX (10.9)
  • Node.js (v0.10.29)
  • npm (1.4.14)
  • express (3.5.1)
  • formidable (1.0.15)
  • jQuery (1.11.0)

準備

まず,express を使用してプロジェクトを生成します.
express -e ejs samplecd sample && sudo npm install

この時,express は app.js に app.use(express.json())app.use(express.urlencoded()) を記述します.
これにより,Content-Type が application/jsonapplication/x-www-form-urlencode のものを解釈するようになります.

ただし,ファイルを含んだPOSTではContent-Typeがmultipart/form-data となるためこのままでは解釈できません.
以前は,app.use(multipart())といったように指定することで対応していましたが,現在はwarningが出るようになっています.

そこで,formidable( https://github.com/felixge/node-formidable )を使用することで対処します.

インストールはnpmからsudo npm install formidable@latestで行います.
実際の動きはサンプルコードを使って説明します.

サンプルコード

post.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>post test</title>
</head>
<body>

<form action="http://localhost:3000/post" method="POST" enctype="multipart/form-data">
    <input type="text" name="name" value="" placeholder="">
    <input type="file" name="file">
    <input type="submit" name="submit" value="送信">
</form>
</body>
</html>

post.html は単純なテキストとファイルをPOSTで送信するものです.
(ほんとはejsとかで用意してapp.jsに追記ってやったほうがいいけどめんどくさかったから端折った)

app.js
/**
 * Module dependencies.
 */

var express = require('express');
var routes = require('./routes');
var user = require('./routes/user');
var http = require('http');
var path = require('path');

var app = express();

// add 1
var formidable = require('formidable');
var fs = require('fs');
var util = require('util');
// ---

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use(express.favicon(__dirname + '/public/favicon/fav.ico'));
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

app.get('/', routes.index);
app.get('/users', user.list);

// add 2
app.post('/post', function(req, res) {

  var form = new formidable.IncomingForm();
  form.encoding = "utf-8";
  form.uploadDir = "./public/images"

  form.parse(req, function(err, fields, files) {
    res.writeHead(200, {'content-type': 'text/html'});
    res.write('received upload:\n\n');
    res.end(util.inspect({fields: fields, files: files}));

    var oldPath = './' + files.file._writeStream.path;
    var newPath = './public/images/' + files.file.name;
    fs.rename(oldPath, newPath, function(err) {
      if (err) throw err;
    });
  });
});
// ---

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

app.js では2つの箇所に追記をしています.
まず,add 1 です.
ここでは,add 2 での処理のために formidableとfs, util を使用する準備をしています.

次に,add 2 です.
add 2 にはlocalhost:3000/post にPOSTが来た時の処理を書いています.

ここでは,まず,文字エンコーディングとファイルをアップロードするディレクトリの指定を行っています.

次に,form.parse で 受け取ったデータの解釈を行います.
form.parse ではpost.html の例だと input type='text' の内容がfields に,input type='file'の内容がfiles に渡されます.
form.parsefunction では,前半3行でクライアントへのレスポンス,
後半で./public/images/に保存されたファイルについて,エンコードされたファイル名から基のファイル名へとリネームを行っています.
filesが持つプロパティ,例えば基のファイル名はfiles.file._writeStream.pathといったように参照できます.

ひとこと

今日からNode.js days にもどるんるん.あ、あとjQuery Ajax で送った時も基本一緒です.

Why do not you register as a user and use Qiita more conveniently?
  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
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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