使用 connect-form 模块时表单未定义
我有一个快速应用程序,我想使用连接表单模块来处理一些文件上传表单。我按照 Express GitHub 存储库和连接表单中的示例进行操作,但是当我调用 req.form.complete(function(){})
时,出现错误,指出我无法调用方法未定义的“完整”
。这是我的代码:
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: [] }
I have an express app that I want to use the connect-form module on to handle some file upload forms. I followed the examples in the express GitHub repo and on the connect-form but when I call req.form.complete(function(){})
I get an error that I Cannot call method 'complete' of undefined
. Here is the code that I have:
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);
And my Jade template:
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")
Is there something I've done wrong with my code? Also when uploading the download field, I actually want to upload the file straight to an S3 bucket. Do I have access to the stream as it is being uploaded so as to have the upload run faster?
Edit
Here is the contents of the req object when I submit the form:
{ 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: [] }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
猜测表单不是从浏览器端提交的,请参阅 http://visionmedia.github.com/ 上提供的示例 示例中的connect-form/
“if (req.form)” 确保表单确实已提交!您应该这样做,但您可能无法控制发布到 API 的内容!
Guess the form is not submitted from brower side, see example provided on http://visionmedia.github.com/connect-form/
"if (req.form)" in the example makes sure that form is indeed submitted ! You ought to do that as well as you may not be able to control what is getting posted to your APIs !!
我不知道为什么 connect-form 不起作用,但我发现它是写在 Formidable 模块之上的,所以我拿了它并在我的路由器中使用它,它正确地选择了表单:
I'm not sure why connect-form didn't work, but I found that it was written on top of the Formidable module, so I took that and just used that in my router instead and it properly picked up the form: