使用连接表单模块时,表单未定义

时间:2011-10-21 14:20:44

标签: node.js express multipartform-data

我有一个快速应用程序,我想使用连接表单模块来处理一些文件上传表单。我按照快速GitHub仓库和连接表单中的示例进行操作,但是当我调用req.form.complete(function(){})时,我收到了一个错误Cannot call method 'complete' of undefined。这是我的代码:

var express = require('express'),
    knox = require('knox'),
    form = require('connect-form')/*,
    controller = require('controller').Controller*/;
var app = module.exports = express.createServer();

// Configuration
var port = process.env.PORT || 3000;

app.configure(function(){
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));

    form({ keepExtensions: true });
});

app.configure('development', function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
});

app.configure('production', function(){
    app.use(express.errorHandler());
});

// Routes
app.get('/', function(req, res){
    res.render('index', {
        title: 'Express'
    });
});

app.get('/manage/new', function(req, res){
    res.render('manage/new', {
        title: 'Create a new widget'
    });
});

app.post('/manage/new', function(req, res, next){
    if(!req.form){
        res.send('Form not submitted.', {'Content-Type': 'text/plain'}, 500);
        console.log(req);
    }
    else{
        req.form.complete(function(err, fields, files){
            if(err) next(err);
            else {
                console.log('\nuploaded %s to %s', files.download.filename, files.download.path);
                res.redirect('back');
            }
        });
        console.log(req.form);
    }
});

app.listen(port);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);

我的Jade模板:

h1= title

#wrapper
    #main
        form(method="post", enctype="multipart/form-data", name="create-widget")
            .errors

            fieldset
                legend Band / Album Information
                p
                    label.tooltip(for="txtTitle", data-tooltip="Band name or title to use on widget.") Band Name
                    br
                    input#txtTitle.input_full(name="title", placeholder="The Beatles")
                p
                    label.tooltip(for="txtAlbum", data-tooltip="Album title shown on widget") Album
                    br
                    input#txtAlbum.input_full(name="album", placeholder="Magical Mystery Tour")
                p
                    label.tooltip(for="fileDownload", data-tooltip="File to be downloaded after submitting form on Widget") Download
                    br
                    input#fileDownload.input_full(name="download", type="file")
            #actions
                input(type="submit", value="Generate Widget")

我的代码有问题吗?另外,在上传下载字段时,我实际上希望将文件直接上传到S3存储桶。我是否可以在上传流时访问该流,以便让上传速度更快?

修改

以下是我提交表单时req对象的内容:

{ socket: 
   { bufferSize: 0,
     fd: 6,
     type: 'tcp4',
     allowHalfOpen: true,
     _readWatcher: 
      { socket: [Circular],
        callback: [Function: onReadable] },
     destroyed: false,
     readable: true,
     _writeQueue: [],
     _writeQueueEncoding: [],
     _writeQueueFD: [],
     _writeQueueCallbacks: [],
     _writeWatcher: 
      { socket: [Circular],
        callback: [Function: onWritable] },
     writable: true,
     _writeImpl: [Function],
     _readImpl: [Function],
     _shutdownImpl: [Function],
     remoteAddress: '127.0.0.1',
     remotePort: 51588,
     server: 
      { stack: [Object],
        connections: 1,
        allowHalfOpen: true,
        watcher: [Object],
        _events: [Object],
        httpAllowHalfOpen: false,
        cache: {},
        settings: [Object],
        redirects: {},
        isCallbacks: {},
        _locals: [Object],
        dynamicViewHelpers: {},
        errorHandlers: [],
        route: '/',
        routes: [Object],
        router: [Getter],
        __usedRouter: true,
        type: 'tcp4',
        fd: 7 },
     ondrain: [Function],
     _idleTimeout: 120000,
     _idleNext: 
      { repeat: 120,
        _idleNext: [Circular],
        _idlePrev: [Circular],
        callback: [Function] },
     _idlePrev: 
      { repeat: 120,
        _idleNext: [Circular],
        _idlePrev: [Circular],
        callback: [Function] },
     _idleStart: Fri, 21 Oct 2011 16:08:07 GMT,
     _events: 
      { timeout: [Function],
        error: [Function],
        close: [Function] },
     ondata: [Function],
     onend: [Function],
     _httpMessage: null },
  connection: 
   { bufferSize: 0,
     fd: 6,
     type: 'tcp4',
     allowHalfOpen: true,
     _readWatcher: 
      { socket: [Circular],
        callback: [Function: onReadable] },
     destroyed: false,
     readable: true,
     _writeQueue: [],
     _writeQueueEncoding: [],
     _writeQueueFD: [],
     _writeQueueCallbacks: [],
     _writeWatcher: 
      { socket: [Circular],
        callback: [Function: onWritable] },
     writable: true,
     _writeImpl: [Function],
     _readImpl: [Function],
     _shutdownImpl: [Function],
     remoteAddress: '127.0.0.1',
     remotePort: 51588,
     server: 
      { stack: [Object],
        connections: 1,
        allowHalfOpen: true,
        watcher: [Object],
        _events: [Object],
        httpAllowHalfOpen: false,
        cache: {},
        settings: [Object],
        redirects: {},
        isCallbacks: {},
        _locals: [Object],
        dynamicViewHelpers: {},
        errorHandlers: [],
        route: '/',
        routes: [Object],
        router: [Getter],
        __usedRouter: true,
        type: 'tcp4',
        fd: 7 },
     ondrain: [Function],
     _idleTimeout: 120000,
     _idleNext: 
      { repeat: 120,
        _idleNext: [Circular],
        _idlePrev: [Circular],
        callback: [Function] },
     _idlePrev: 
      { repeat: 120,
        _idleNext: [Circular],
        _idlePrev: [Circular],
        callback: [Function] },
     _idleStart: Fri, 21 Oct 2011 16:08:07 GMT,
     _events: 
      { timeout: [Function],
        error: [Function],
        close: [Function] },
     ondata: [Function],
     onend: [Function],
     _httpMessage: null },
  httpVersion: '1.1',
  complete: false,
  headers: 
   { host: '127.0.0.1:5000',
     'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:6.0) Gecko/20100101 Firefox/6.0',
     accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
     'accept-language': 'en-us,en;q=0.5',
     'accept-encoding': 'gzip, deflate',
     'accept-charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
     connection: 'keep-alive',
     referer: 'http://127.0.0.1:5000/manage/new',
     'content-type': 'multipart/form-data; boundary=---------------------------20072377098235644401115438165',
     'content-length': '247075' },
  trailers: {},
  readable: true,
  url: '/manage/new',
  method: 'POST',
  statusCode: null,
  client: 
   { bufferSize: 0,
     fd: 6,
     type: 'tcp4',
     allowHalfOpen: true,
     _readWatcher: 
      { socket: [Circular],
        callback: [Function: onReadable] },
     destroyed: false,
     readable: true,
     _writeQueue: [],
     _writeQueueEncoding: [],
     _writeQueueFD: [],
     _writeQueueCallbacks: [],
     _writeWatcher: 
      { socket: [Circular],
        callback: [Function: onWritable] },
     writable: true,
     _writeImpl: [Function],
     _readImpl: [Function],
     _shutdownImpl: [Function],
     remoteAddress: '127.0.0.1',
     remotePort: 51588,
     server: 
      { stack: [Object],
        connections: 1,
        allowHalfOpen: true,
        watcher: [Object],
        _events: [Object],
        httpAllowHalfOpen: false,
        cache: {},
        settings: [Object],
        redirects: {},
        isCallbacks: {},
        _locals: [Object],
        dynamicViewHelpers: {},
        errorHandlers: [],
        route: '/',
        routes: [Object],
        router: [Getter],
        __usedRouter: true,
        type: 'tcp4',
        fd: 7 },
     ondrain: [Function],
     _idleTimeout: 120000,
     _idleNext: 
      { repeat: 120,
        _idleNext: [Circular],
        _idlePrev: [Circular],
        callback: [Function] },
     _idlePrev: 
      { repeat: 120,
        _idleNext: [Circular],
        _idlePrev: [Circular],
        callback: [Function] },
     _idleStart: Fri, 21 Oct 2011 16:08:07 GMT,
     _events: 
      { timeout: [Function],
        error: [Function],
        close: [Function] },
     ondata: [Function],
     onend: [Function],
     _httpMessage: null },
  httpVersionMajor: 1,
  httpVersionMinor: 1,
  upgrade: false,
  originalUrl: '/manage/new',
  query: {},
  app: 
   { stack: 
      [ [Object],
        [Object],
        [Object],
        [Object],
        [Object],
        [Object] ],
     connections: 1,
     allowHalfOpen: true,
     watcher: { host: [Circular], callback: [Function] },
     _events: 
      { request: [Function],
        connection: [Function: connectionListener],
        listening: [Function] },
     httpAllowHalfOpen: false,
     cache: {},
     settings: 
      { env: 'development',
        hints: true,
        views: '/Users/davejlong/Workspace/Node/Widget/views',
        'view engine': 'jade' },
     redirects: {},
     isCallbacks: {},
     _locals: { settings: [Object], app: [Circular] },
     dynamicViewHelpers: {},
     errorHandlers: [],
     route: '/',
     routes: 
      { app: [Circular],
        routes: [Object],
        params: {},
        _params: [],
        middleware: [Function] },
     router: [Getter],
     __usedRouter: true,
     type: 'tcp4',
     fd: 7 },
  res: 
   { output: [],
     outputEncodings: [],
     writable: true,
     _last: false,
     chunkedEncoding: false,
     shouldKeepAlive: true,
     useChunkedEncodingByDefault: true,
     _hasBody: true,
     _trailer: '',
     finished: true,
     socket: null,
     connection: null,
     _events: { finish: [Function] },
     _headers: 
      { 'x-powered-by': 'Express',
        'content-type': 'text/plain; charset=utf-8',
        'content-length': 19 },
     _headerNames: 
      { 'x-powered-by': 'X-Powered-By',
        'content-type': 'Content-Type',
        'content-length': 'Content-Length' },
     app: 
      { stack: [Object],
        connections: 1,
        allowHalfOpen: true,
        watcher: [Object],
        _events: [Object],
        httpAllowHalfOpen: false,
        cache: {},
        settings: [Object],
        redirects: {},
        isCallbacks: {},
        _locals: [Object],
        dynamicViewHelpers: {},
        errorHandlers: [],
        route: '/',
        routes: [Object],
        router: [Getter],
        __usedRouter: true,
        type: 'tcp4',
        fd: 7 },
     req: [Circular],
     charset: 'utf-8',
     statusCode: 500,
     _header: 'HTTP/1.1 500 Internal Server Error\r\nX-Powered-By: Express\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: 19\r\nConnection: keep-alive\r\n\r\n',
     _headerSent: true },
  next: [Function: next],
  originalMethod: 'POST',
  _route_index: 0,
  route: 
   { path: '/manage/new',
     method: 'post',
     callbacks: [ [Function] ],
     keys: [],
     regexp: /^\/manage\/new\/?$/i,
     params: [] },
  params: [] }

2 个答案:

答案 0 :(得分:0)

猜猜表单不是从浏览方提交的,请参阅http://visionmedia.github.com/connect-form/

上提供的示例
 var form = require('connect-form');
 var server = connect.createServer(
        form({ keepExtensions: true }),
        function(req, res, next){
            // Form was submitted
        if (req.form) {
            // Do something when parsing is finished
            // and respond, or respond immediately
            // and work with the files.
            req.form.onComplete = function(err, fields, files){
                res.writeHead(200, {});
                if (err) res.write(JSON.stringify(err.message));
                res.write(JSON.stringify(fields));
                res.write(JSON.stringify(files));
                res.end();
            };
        // Regular request, pass to next middleware
        } else {
            next();
        }
    }
);

示例中的“if(req.form)”确保表单确实已提交!您应该这样做,因为您可能无法控制发布到您的API的内容!!

答案 1 :(得分:0)

我不确定为什么连接表单不起作用,但是我发现它是在Formidable模块的顶部写的,所以我把它拿到了我的路由器中然后用它来正确地拾取表单:

var sys = require('sys'),
    express = require('express'),
    knox = require('knox'),
    formidable = require('formidable'),
    controller = require('./controller').Controller;
var app = module.exports = express.createServer();

/* ================================================================ */
/*                          Configuration                           */
/* ================================================================ */
var port = process.env.PORT || 3000;
var hostName = 'http://download.comeandlive.com';
controller.setup(hostName, 80);

app.configure(function(){
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); 
});

app.configure('production', function(){
    app.use(express.errorHandler());
});

/* ================================================================ */
/*                                  Routes                          */
/* ================================================================ */
app.get('/', function(req, res){
    res.redirect('http://comeandlive.com', 301);
});

/* ================================ */
/*      Administration Routes       */
/* ================================ */
app.get('/manage', function(req, res){
    res.render('manage/index', {
        title: 'Widget Administration'
    });
})
app.get('/manage/new', function(req, res){
    res.render('manage/new', {
        title: 'Create a new widget'
    });
});

app.post('/manage/new', function(req, res, next){
    var form = new formidable.IncomingForm();
    form.parse(req, function(err, fields, files){
        res.header('Content-Type', 'text/plain');
        res.send('Received upload:\n\n' + sys.inspect({fields:fields, files:files}));
    });
});


app.listen(port);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);