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

備忘録です．
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 sample` ，`cd sample && sudo npm install`

この時，express は　app.js に `app.use(express.json())` と `app.use(express.urlencoded())` を記述します．
これにより，Content-Type が `application/json` と `application/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`で行います．
実際の動きはサンプルコードを使って説明します．

# サンプルコード
```html: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に追記ってやったほうがいいけどめんどくさかったから端折った）

```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.parse` の `function` では，前半3行でクライアントへのレスポンス，
後半で`./public/images/`に保存されたファイルについて，エンコードされたファイル名から基のファイル名へとリネームを行っています．
filesが持つプロパティ，例えば基のファイル名は`files.file._writeStream.path`といったように参照できます．

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