pig's diary

何でも忘れるので万年初心者ね

express でファイルアップロード

参考:

nodeでファイルアップロードする方法、難しい順。

1. req.on('data') で、ちょこちょこデータを溜め込んでいく。(僕には無理っぽい)
2. それを楽にするformidable というモジュール。丁寧に 'progress' というイベントまでemit してくれる。
3. express のapp.configure で app.use(express.bodyParser()) していたら、req.files で更に簡単にupload できる。formidable は必要なくなる。

express でファイルアップロードを書く

ただアップロードする他に、ファイルを配置するディレクトリを作ったり、DBにファイルを保存したりしてみました。
でも、ほとんどhttp://www.hacksparrow.com/handle-file-uploads-in-express-node-js.htmlの内容です。
html:

<form action="/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="upfile"/>
  <input type="submit" value="送信"/>
</form>

server:

/* app.js */
var TMP_DIR = './tmp';
app.configure(function(){
  .
  .
  app.use(express.bodyParser({
    uploadDir: TMP_DIR /* 追記 */
  }));
  .
  .
});
.
.
app.get('/', routes.index);
app.post('/upload', routes.upload); /* 追記 */
.
/* route/index.js */

/*
 * GET home page.
 */


// Require modules.
var mkdirp = require('mkdirp') /* $ npm install mkdirp */
  , fs = require('fs')
  , _ = require('underscore')
  , path = require('path')
  , db = require('../resources/server/db');



// Pad string function.
_.mixin({
  pad: function (target, length) {
    var padStr = '0';
    var target = target.toString();
    var result = target;
    var num = length - target.length;
    for (var i=0; i<num; i++) {
      result = padStr + result;
    }
    return result;
  }
});



// Handler for GET '/'.
exports.index = function(req, res){
  db.Files.find({}, [], {}, function (err, files) {
    if (err) return;
    res.render('index', { 
      title: 'Express'
    , IS_PRODUCTION: process.env.NODE_ENV === 'production' // new
    , files: files ? files : []
    })
  });
}


// Handler for POST '/upload'.
var UPLOAD_BASEPATH = __dirname + '/../public/uploaded/';

var getUpfileDir = function () {
  var d = new Date();
  return d.getFullYear() + _.pad(d.getMonth() + 1, 2) + '/' + _.pad(d.getDate() + '/', 2);
};

exports.upload = function(req, res){
  var upfile = req.files.upfile
  if (upfile) {
    var tmpPath = './' + upfile.path;
    var saveDir = getUpfileDir();
    var dir_ = path.normalize(UPLOAD_BASEPATH + saveDir);
    if (!path.existsSync(dir_)) mkdirp.sync(dir_);
    var savePath = saveDir + path.basename(tmpPath) + path.extname(upfile.name);
    var targetPath = path.normalize(UPLOAD_BASEPATH + savePath);

    fs.rename(tmpPath, targetPath, function (err) {
      if (err) throw err;
      var file = new db.Files();
      file = _.extend(file, {
        title: req.body.title
      , name: upfile.name
      , type: upfile.type
      , path: savePath
      , createdAt: new Date()
      });
      file.save(function (err) {
        res.redirect('/');
      });
    });
  }
};
/* resources/server/db.js */

// Setting mongoose.
var mongoose = require('mongoose');
var FileUploadSchema = new mongoose.Schema({
  title: String
, name: String
, type: String
, path: String
, createdAt: Date
});

mongoose.model('Files', FileUploadSchema)
mongoose.connect('mongodb://localhost/fileupload')

exports.Files = mongoose.model('Files')

その他

  • <input type="file" name="upfile" multiple="multiple" / >と書くと、複数ファイルを同時にアップロードできる(対応ブラウザが限られると思います、ご注意ください)
  • その場合は req.files.upfile がオブジェクトじゃなくて配列になる。