一般咱們用Ajax上傳文件的時候都會用到FormData
,這種方式比較簡單。今天介紹一種用純Ajax上傳文件的方式
表單數據能夠用一下四種方式進行發送:
1.method:POST,enctype:application/x-www-form-urlencoded (默認編碼方式);
2.method:POST,enctype:text/plain;
3.method:POST,enctype:multipart/form-data;
4.method:GET(enctype屬性會被忽略).
若表單內容以下:node
<form action="xx" method="xx" enctype="xxx" >
foo: <input type="text" name="foo" />
baz: <input type="text" name="baz" />
</form>
複製代碼
則對於以上四種表單提交方式服務端收到的內容依次對應以下:git
Content-Type: application/x-www-form-urlencoded
foo=bar&baz=The+first+line.%0D%0AThe+second+line.%0D%0A
複製代碼
Content-Type: text/plain
foo=bar
baz=The first line.
The second line.
複製代碼
Content-Type: multipart/form-data; boundary=---------------------------314911788813839
-----------------------------314911788813839
Content-Disposition: form-data; name="foo"
bar
-----------------------------314911788813839
Content-Disposition: form-data; name="baz"
The first line.
The second line.
-----------------------------314911788813839--
複製代碼
?foo=bar&baz=The%20first%20line.%0AThe%20second%20line.
複製代碼
咱們要實現本身的用Ajax進行表單提交,就要根據表單的method和enctype組裝出提交給服務器的內容。涉及到新一些的內容有FileReader
和TypedArray
具體實現以下:github
var AJAXSubmit = (function () {
if (!XMLHttpRequest.prototype.sendAsBinary) {
XMLHttpRequest.prototype.sendAsBinary = function (sData) {
var nBytes = sData.length, ui8Data = new Uint8Array(nBytes);
for (var nIdx = 0; nIdx < nBytes; nIdx++) {
ui8Data[nIdx] = sData.charCodeAt(nIdx) & 0xff;
}
this.send(ui8Data);
};
}
function SubmitRequest(oTarget) {
var nFile, sFieldType, oField, oSegmReq, oFile, bIsPost = oTarget.method.toLowerCase() === "post";
this.contentType = bIsPost && oTarget.enctype ? oTarget.enctype : "application\/x-www-form-urlencoded";
this.technique = bIsPost ?
this.contentType === "multipart\/form-data" ? 3 : this.contentType === "text\/plain" ? 2 : 1 : 0;
this.receiver = oTarget.action;
this.status = 0;
this.segments = [];
for (var nItem = 0; nItem < oTarget.elements.length; nItem++) {
oField = oTarget.elements[nItem];
if (!oField.hasAttribute("name")) { continue; }
sFieldType = oField.nodeName.toUpperCase() === "INPUT" ? oField.getAttribute("type").toUpperCase() : "TEXT";
if (sFieldType === "FILE" && oField.files.length > 0) {
if (this.technique === 3) {
/* enctype is multipart/form-data */
for (nFile = 0; nFile < oField.files.length; nFile++) {
oFile = oField.files[nFile];
oSegmReq = new FileReader();
/* (custom properties:) */
oSegmReq.segmentIdx = this.segments.length;
oSegmReq.owner = this;
/* (end of custom properties) */
oSegmReq.onload = pushSegment;
this.segments.push("Content-Disposition: form-data; name=\"" +
oField.name + "\"; filename=\"" + oFile.name +
"\"\r\nContent-Type: " + oFile.type + "\r\n\r\n");
this.status++;
oSegmReq.readAsBinaryString(oFile);
}
} else {
/* enctype is application/x-www-form-urlencoded or text/plain or
method is GET: files will not be sent! */
for (nFile = 0; nFile < oField.files.length;
this.segments.push(escape(oField.name) + "=" + escape(oField.files[nFile++].name)));
}
} else if ((sFieldType !== "RADIO" && sFieldType !== "CHECKBOX") || oField.checked) {
this.segments.push(
this.technique === 3 ? /* enctype:multipart/form-data */
"Content-Disposition: form-data; name=\"" + oField.name + "\"\r\n\r\n" + oField.value + "\r\n"
: /* enctype :application/x-www-form-urlencoded or text/plain or method :GET */
escape(oField.name) + "=" + escape(oField.value)
);
}
}
processStatus(this);
}
function ajaxSuccess() {
console.log(this.responseText);
}
function submitData(oData) {
/* the AJAX request... */
var oAjaxReq = new XMLHttpRequest();
oAjaxReq.submittedData = oData;
oAjaxReq.onload = ajaxSuccess;
if (oData.technique === 0) {
/* method:GET */
oAjaxReq.open("get", oData.receiver.replace(/(?:\?.*)?$/,
oData.segments.length > 0 ? "?" + oData.segments.join("&") : ""), true);
oAjaxReq.send(null);
} else {
/* method:POST */
oAjaxReq.open("post", oData.receiver, true);
if (oData.technique === 3) {
/* enctype:multipart/form-data */
var sBoundary = "---------------------------" + Date.now().toString(16);
oAjaxReq.setRequestHeader("Content-Type", "multipart\/form-data; boundary=" + sBoundary);
oAjaxReq.sendAsBinary("--" + sBoundary + "\r\n" +
oData.segments.join("--" + sBoundary + "\r\n") + "--" + sBoundary + "--\r\n");
} else {
/* enctype is application/x-www-form-urlencoded or text/plain */
oAjaxReq.setRequestHeader("Content-Type", oData.contentType);
oAjaxReq.send(oData.segments.join(oData.technique === 2 ? "\r\n" : "&"));
}
}
}
function processStatus(oData) {
if (oData.status > 0) { return; }
submitData(oData);
}
function pushSegment(oFREvt) {
this.owner.segments[this.segmentIdx] += oFREvt.target.result + "\r\n";
this.owner.status--;
processStatus(this.owner);
}
return function (oFormElement) {
if (!oFormElement.action) { return; }
new SubmitRequest(oFormElement);
};
})();
複製代碼
使用:ajax
<form action="xx" method="post" enctype="multipart/form-data" onsubmit="AJAXSubmit(this); return false;" >
foo: <input type="text" name="foo" />
baz: <input type="text" name="baz" />
</form>
複製代碼
const express = require('express')
const path=require('path')
const bodyParser = require('body-parser')
const multer = require('multer')
const app = express()
const upload = multer({ dest: path.join(__dirname, '/uploads/') })
app.get('/testGet', function (req, res) {
console.log(req.query);
res.send('success');
})
// create application/x-www-form-urlencoded parser
app.post('/testPostDefault', bodyParser.urlencoded({ extended: false }),function (req, res) {
console.log(req.body);
res.send('success');
})
app.post('/testPostText', bodyParser.text({ type: 'text/plain' }),function (req, res) {
console.log(req.body);
res.send('success');
})
app.post('/testPostMulipart',upload.array('photos', 12), function (req, res) {
console.log(req.files);
console.log(req.body);
res.send('success');
})
app.use(express.static(path.join(__dirname, 'www')));
app.listen(3000,function(){
console.log('Server running at http://localhost:3000');
})
複製代碼
經測試對於表單的四種提交,後端都能正確的接受到相應的數據
代碼地址:github.com/fanxuewen/e…express