web端:
var xhr = new XMLHttpRequest();
/**
* handle callback
*/
xhr.onreadystatechange = onreadystatechange;
/**
* progress
*/
xhr.upload.onprogress = handleProgress;
/**
* true is using async; sync is deprecated
*/
xhr.open('POST', uri, true);
/**
* form data
*/
xhr.send(filedata);
服务器端(nodejs):
var formidable = require('formidable'),
http = require('http'),
util = require('util');
http.createServer(function (req, res) {
console.log('get the req');
console.log('req ::' + req);
if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
// parse a file upload
var form = new formidable.IncomingForm();
form.parse(req, function (err, fields, files) {
res.writeHead(200, {
'Access-Control-Allow-Methods': 'OPTIONS, HEAD, POST',
'Access-Control-Allow-Origin': '*',
});
res.write('received upload:\n\n');
res.end(util.inspect({ fields: fields, files: files }));
});
return;
} else if (req.url == '/upload' && req.method.toLowerCase() == 'options') {
// parse a file upload
res.writeHead(200, {
'Access-Control-Allow-Methods': 'OPTIONS, HEAD, POST',
'Access-Control-Allow-Origin': '*',
});
res.write('received upload:\n\n');
res.end();
return;
}
}).listen(9000);
这里有个问题就是当跨域监听上传进度的时候,会抛出prelight no ‘Access-control-allow-origin’in response之类的错误,
这是因为webkit和firefox浏览器会自动发送一个OPTIONS的请求到服务器。服务器需要对这个请求也设置成跨域的。
I ended up checking out the Webkit source code in an attempt to figure this out (after Google did not yield any helpful hits). It turns out that Webkit will force any cross-origin request to be preflighted simply if you register an onprogress event handler. I’m not entirely sure, even after reading the code comments, why this logic was applied.
In XMLHttpRequest.cpp:
void XMLHttpRequest::createRequest(ExceptionCode& ec)
{
…
options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
...
// The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
// permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
// Also, only async requests support upload progress events.
bool uploadEvents = false;
if (m_async) {
m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
if (m_requestEntityBody && m_upload) {
uploadEvents = m_upload->hasEventListeners();
m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
}
}
...
}
UPDATE: Firefox applies the same logic as Webkit, it appears. Here is the relevant code from nsXMLHttpRequest.cpp:
nsresult
nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
{
…
// Check if we need to do a preflight request.
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);
nsAutoCString method;
httpChannel->GetRequestMethod(method);
if (!mCORSUnsafeHeaders.IsEmpty() ||
(mUpload && mUpload->HasListeners()) ||
(!method.LowerCaseEqualsLiteral("get") &&
!method.LowerCaseEqualsLiteral("post") &&
!method.LowerCaseEqualsLiteral("head"))) {
mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
}
...
}
Notice the mUpload && mUpload->HasListeners() portion of the conditional.
Seems like Webkit and Firefox (and possibly others) have inserted some logic into their preflight-determination code that is not sanctioned by the W3C spec. If I’m missing something in the spec, please comment.
同时如果后台使用的是springMVC,要在web.xml中添加下述配置
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>