You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
3.0 KiB

  1. /*!
  2. * Connect - multipart
  3. * Copyright(c) 2010 Sencha Inc.
  4. * Copyright(c) 2011 TJ Holowaychuk
  5. * MIT Licensed
  6. */
  7. /**
  8. * Module dependencies.
  9. */
  10. var formidable = require('formidable')
  11. , _limit = require('./limit')
  12. , utils = require('../utils')
  13. , qs = require('qs');
  14. /**
  15. * noop middleware.
  16. */
  17. function noop(req, res, next) {
  18. next();
  19. }
  20. /**
  21. * Multipart:
  22. *
  23. * Parse multipart/form-data request bodies,
  24. * providing the parsed object as `req.body`
  25. * and `req.files`.
  26. *
  27. * Configuration:
  28. *
  29. * The options passed are merged with [formidable](https://github.com/felixge/node-formidable)'s
  30. * `IncomingForm` object, allowing you to configure the upload directory,
  31. * size limits, etc. For example if you wish to change the upload dir do the following.
  32. *
  33. * app.use(connect.multipart({ uploadDir: path }));
  34. *
  35. * Options:
  36. *
  37. * - `limit` byte limit defaulting to none
  38. * - `defer` defers processing and exposes the Formidable form object as `req.form`.
  39. * `next()` is called without waiting for the form's "end" event.
  40. * This option is useful if you need to bind to the "progress" event, for example.
  41. *
  42. * @param {Object} options
  43. * @return {Function}
  44. * @api public
  45. */
  46. exports = module.exports = function(options){
  47. options = options || {};
  48. var limit = options.limit
  49. ? _limit(options.limit)
  50. : noop;
  51. return function multipart(req, res, next) {
  52. if (req._body) return next();
  53. req.body = req.body || {};
  54. req.files = req.files || {};
  55. if (!utils.hasBody(req)) return next();
  56. // ignore GET
  57. if ('GET' == req.method || 'HEAD' == req.method) return next();
  58. // check Content-Type
  59. if ('multipart/form-data' != utils.mime(req)) return next();
  60. // flag as parsed
  61. req._body = true;
  62. // parse
  63. limit(req, res, function(err){
  64. if (err) return next(err);
  65. var form = new formidable.IncomingForm
  66. , data = {}
  67. , files = {}
  68. , done;
  69. Object.keys(options).forEach(function(key){
  70. form[key] = options[key];
  71. });
  72. function ondata(name, val, data){
  73. if (Array.isArray(data[name])) {
  74. data[name].push(val);
  75. } else if (data[name]) {
  76. data[name] = [data[name], val];
  77. } else {
  78. data[name] = val;
  79. }
  80. }
  81. form.on('field', function(name, val){
  82. ondata(name, val, data);
  83. });
  84. form.on('file', function(name, val){
  85. ondata(name, val, files);
  86. });
  87. form.on('error', function(err){
  88. if (!options.defer) {
  89. err.status = 400;
  90. next(err);
  91. }
  92. done = true;
  93. });
  94. form.on('end', function(){
  95. if (done) return;
  96. try {
  97. req.body = qs.parse(data);
  98. req.files = qs.parse(files);
  99. if (!options.defer) next();
  100. } catch (err) {
  101. form.emit('error', err);
  102. }
  103. });
  104. form.parse(req);
  105. if (options.defer) {
  106. req.form = form;
  107. next();
  108. }
  109. });
  110. }
  111. };