AJAX傳輸二進位制資料
阿新 • • 發佈:2020-10-21
FormData物件
- 將整個表單元素對映成一個物件,可實現自動拼接表單物件資料成請求引數的格式
- 可以上傳二進位制資料
利用FormData上傳文字資料的表單:
前端網頁核心程式碼:
<form id="myform"> <!-- form屬於塊元素 --> 使用者名稱 = <input type="text" name="username" value=""> <br> 密碼 = <input type="password" name="pwd" value=""> <br> <input type="button" name="go" value="提交" onclick="doit()"> </form> <script> "use strict" var myform = document.getElementById('myform'); var formdata; // 如果現在執行`formdata = new FormData(myform)`會導致formdata中的表單資料是空 function doit(){ formdata = new FormData(myform); // FormDate API:https://developer.mozilla.org/zh-CN/docs/Web/API/FormData var xhr = new XMLHttpRequest(); if (formdata.get('username') == 'nat'){ // IE11支援FormData構造器但是不支援get、set等一些方法 formdata.set('usernameOrigin', 'nat'); formdata.set('username', 'natasha'); // 同名的鍵會覆蓋 formdata.delete('pwd'); formdata.append('username', 'natashe'); // 同名的鍵會保留 } for (let x of formdata){ console.log(x); // 如果'username'傳入的是'nat'那麼輸出如下: // Array [ "username", "natasha" ] // Array [ "usernameOrigin", "nat" ] // Array [ "username", "natashe" ] // 如果伺服器不進行特殊設定,那麼username只能獲取到最後一次的值,即'natashe'(設定略) } xhr.open('POST', '/postdata'); xhr.send(formdata); // 會自動將'Content-Type'設定成'multipart/form-data; boundary=---------------------------20808984378436579741074229525' // 注意,FormData資料在Node中不能通過中介軟體body-parser獲取,嘗試通過`req.body`獲取始終返回空物件,而是通過`cnpm install --save formidable`這個模組獲取,注意,它不是中介軟體 xhr.onload = function(){ if (xhr.status == 200){ console.log(xhr.responseText); }else{ console.log('Something error happened: ', xhr); } } } </script>
後端Node核心程式碼:
const formidable = require('formidable');
app.post('/postdata', (req,res)=>{
var form = new formidable.IncomingForm();
form.parse(req, (err, fields, files)=>{
// fields是普通的表單資料(文字)
// files是上傳的檔案資料
console.log(fields);
res.send({id:20, data:[22,11,14,24]});
});
});
利用FormData上傳二進位制資料的本地檔案:
前端網頁核心程式碼:
<form id="myform"> 暱稱 = <input type="text" name="nickname"> <br> 檔案 = <input type="file" name="doc"> <br> <input type="button" name="go" value="提交" onclick="doit()"> </form> <script> "use strict" var myform = document.getElementById('myform'); var formdata; function doit(){ formdata = new FormData(myform); var xhr = new XMLHttpRequest(); xhr.open('POST', '/postdata'); xhr.upload.onprogress = function(e){ // 這個事件必須在send之前監聽有效,在send之後監聽不會觸發 console.log(`當前進度:${Math.floor(e.loaded / e.total * 100)}%`); } xhr.send(formdata); xhr.onload = function(){ if (xhr.status == 200){ console.log(xhr.responseText); }else{ console.log('Something error happened: ', xhr); } } } </script>
後端Node核心程式碼:
app.post('/postdata', (req,res)=>{
var form = new formidable.IncomingForm();
form.uploadDir = './webroot/uploads'; // 上傳的檔案儲存在哪個目錄
form.keepExtensions = true; // 保留上傳時的字尾名
form.parse(req, (err, fields, files)=>{
// fields是普通的表單資料(文字)
// files是上傳的檔案資料
console.log(fields);
console.log(files.doc.path); // 'webroot\\uploads\\upload_4242811d6678445365bbc7c55c0a968f.dat'
res.send({id:20, data:[22,11,14,24]});
});
});
XHR上傳和下載二進位制資料(TypedArray和Blob)
TypedArray
前端傳給後端
// 前端
var data = new ArrayBuffer(64);
var dataview = new Uint8Array(data);
for (let i=0; i<data.byteLength-1; i++){
dataview[i] = 97 + (i % 26); // 將64位元組的二進位制資料初始化成[a-z]的小寫字母
}
dataview[data.byteLength-1] = 0;
function doit(){
var xhr = new XMLHttpRequest();
xhr.open('POST', '/postdata');
// xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // 表示通用二進位制流,可不寫
xhr.send(data);
xhr.onload = function(){
if (xhr.status == 200){
console.log(xhr.responseText);
}else{
console.log('Something error happened: ', xhr);
}
}
}
// 通過抓包,send出去的報文就是ArrayBuffer的二進位制資料
// Node原生http伺服器收到的POST資料預設是Buffer型別
// 後端
app.post('/postdata', (req,res)=>{
var tmpchunk = [];
var data;
req.on('data', (chunk)=>{
tmpchunk.push(chunk);
});
req.on('end', ()=>{
data = Buffer.concat(tmpchunk);
console.log(data);
res.send('ok');
})
});
後端傳給前端
// 前端
function doit(){
var xhr = new XMLHttpRequest();
xhr.open('GET', '/getdata');
xhr.responseType = 'arraybuffer';
xhr.send();
xhr.onload = function(){
if (xhr.status == 200){
console.log(xhr.response); // ArrayBuffer 位元組長度:8
}else{
console.log('Something error happened: ', xhr);
}
}
}
// 後端
app.get('/getdata', (req,res)=>{
res.set('Content-Type','application/octet-stream');
var data = new ArrayBuffer(8);
var dataview = new Uint8Array(data);
dataview[1] = 11;
dataview[3] = 22;
dataview[5] = 33;
dataview[7] = 44;
data = Buffer.from(data); // 將ArrayBuffer轉換成Buffer
res.send(data); // 如果直接返回ArrayBuffer,會被JSON化成'{}'(注意,實際沒有單引號,這裡只做分隔符),只能傳入Buffer才能使send方法正確傳送二進位制資料,通過抓包得到的響應體的資料是:00 0b 00 16 00 21 00 2c
});
Blob
前端傳給後端
var data = new Blob(['haha']);
function doit(){
var xhr = new XMLHttpRequest();
xhr.open('POST', '/postdata');
// xhr.setRequestHeader('Content-Type', 'application/octet-stream'); // 表示通用二進位制流,可不寫
xhr.send(data);
xhr.onload = function(){
if (xhr.status == 200){
console.log(xhr.responseText);
}else{
console.log('Something error happened: ', xhr);
}
}
}
// 原理同ArrayBuffer,send出去的報文就是Blob的二進位制資料,報文的請求體:h a h a
後端傳給前端
// 注意,Node不存在Blob,只需要傳遞Buffer即可,故此後端程式碼同【TypedArray的後端傳給前端】
// 前端:將xhr.responseType = 'arraybuffer' -> 'blob',獲取到的xhr.response返回`Blob{size: 8, type: "application/json"}`,其中的type會被自動設定成響應頭中的'Content-Type'
舊時代的二進位制傳輸
在xhr.responseType還沒有'arraybuffer'、'blob'甚至'json'的時代,B/S傳遞二進位制只能通過表單上傳和檔案下載,AJAX還沒有傳遞二進位制的API,但是可以hack出來,如下,
// 前端
function doit(){
var xhr = new XMLHttpRequest();
xhr.open('GET', '/getdata');
// xhr.overrideMimeType('Content-Type', 'text/plain; charset=x-user-defined'); // 告訴瀏覽器不要嘗試去解析字串,不加這行其實也行
xhr.send();
xhr.onload = function(){
if (xhr.status == 200){
// console.log(xhr.getAllResponseHeaders()); // 即便上面已經覆蓋了,但是獲取到的還是原始的響應頭
// debugger
var data = xhr.response;
var len = data.length;
console.log(len); // 不能直接輸出data,會亂碼,嘗試檢測收到的資料的長度,輸出4,正確
var received = new ArrayBuffer(len);
var receivedview = new Uint8Array(received);
for (let i=0; i<len; i++){
receivedview[i] = data.charCodeAt(i);
}
console.log(receivedview); // 輸出Uint8Array(6)[70, 126, 127, 253, 253, 253]
// 得出結論:採用這種方式後端只能傳遞[0, 127]範圍的位元組
}else{
console.log('Something error happened: ', xhr);
}
}
}
// 後端
app.get('/getdata', (req,res)=>{
var data = Buffer.allocUnsafe(4);
data[0] = 70; data[1] = 126; data[2] = 127; data[3] = 128; data[4] = 129; data[5] = 255;
res.send(data);
});