Node.js HTTP服務(wù)器中的文件、圖片上傳的方法
HTTP協(xié)議中,multipart/form-data格式用于向服務(wù)器發(fā)送二進(jìn)制數(shù)據(jù),通過這一內(nèi)容類型(Content-Type)可以實(shí)現(xiàn)文件、圖片的上傳。由于這種格式發(fā)送的是二進(jìn)制數(shù)據(jù),在服務(wù)器端接收和處理數(shù)據(jù)時會與其它內(nèi)容類型有所有區(qū)別。
HTTP協(xié)議中的文件上傳
最早的HTTP協(xié)議中是不支持文件上傳的,在1995年制定的rfc1867規(guī)范中,在HTTP POST請求的內(nèi)容類型Content-Type中擴(kuò)展了multipart/form-data類型,該類型用于向服務(wù)器發(fā)送二進(jìn)制數(shù)據(jù),以便支持文件的上傳。
POST上傳文件
我們通過form表單提交文件時,會構(gòu)造類似像下面這樣一個表單:
<form enctype="multipart/form-data" action="_URL_" method="POST"> <input name="userfile1" type="file"> <input type="submit" value="發(fā)送文件"> </form>
在使用form提交表單數(shù)據(jù)時,默認(rèn)的編碼格式為application/x-www-form-urlencoded,上傳文件時需要通過enctype屬性將編碼方式設(shè)置為multipart/form-data。
HTTP數(shù)據(jù)提交與服務(wù)器數(shù)據(jù)解析
在包含請求體的請求中,提交的數(shù)據(jù)會按指定編碼類型進(jìn)行編碼,而客戶端會按編碼方式設(shè)置請求頭中的Content-Type字段。
在一個application/x-www-form-urlencoded編碼的請求中,會設(shè)置一個如下的請求頭:
Content-Type:application/x-www-form-urlencoded
而用于文件上傳的編碼方式multipart/form-data,會設(shè)置一個如下的請求頭:
Content-type: multipart/form-data, boundary=AaB03x
服務(wù)器數(shù)據(jù)接收與解析
對于一個編碼方式為application/x-www-form-urlencoded的請求來說,會對提交內(nèi)容進(jìn)行URL編碼。服務(wù)器會收到類似如下內(nèi)容:
POST / HTTP/1.1 Content-Type: application/x-www-form-urlencoded Accept-Encoding: gzip, deflate Host: itbilu.com Content-Length: 23 Connection: Keep-Alive Cache-Control: max-age=0 key1=value1&key2=value2
請求頭與請求體之間會有一個空行,服務(wù)器會對請求體以queryString的方式進(jìn)行解碼。
而對一個multipart/form-data的文件上傳請求來說,收到的內(nèi)容類似如下:
POST / HTTP/1.1 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryYN9YYwO9ESipYBIx Accept-Encoding: gzip, deflate Host: itbilu.com Content-Length: 22646 Connection: Keep-Alive Cache-Control: max-age=0 ------WebKitFormBoundaryoqBx9oYBhx4SF1YQ Content-Disposition: form-data; name="myName" itbilu.com ------WebKitFormBoundaryYN9YYwO9ESipYBIx Content-Disposition: form-data; name="upload"; filename="41GiLecHO3L.jpg" Content-Type: image/jpeg ����JFIF��C // 文件的二進(jìn)制數(shù)據(jù) …… --------WebKitFormBoundaryYN9YYwO9ESipYBIx--
在請求頭的Content-Type字段中,除了編碼類型為multipart/form-data描述外,還有一個boundary屬性,這是客戶端隨機(jī)生成的一個數(shù)據(jù)邊界描述。
如上所示,文件上傳時內(nèi)容是分段傳輸?shù)?,每一boundary表示一個fild(form表單控值)邊界。
如上面示例所示,上傳文件時除內(nèi)容描述外還包含一個的Content-Type文件MIME的描述,其后是一個空行和文件的二進(jìn)制數(shù)據(jù)。所有的表單數(shù)據(jù)結(jié)束后,會有一個”–”+boundary+”–”結(jié)束符。而服務(wù)器接收到數(shù)據(jù)后,同樣會根據(jù)boundary來進(jìn)行數(shù)據(jù)的接收和解析。
Node.js中處理圖片/文件上傳
Node.js中處理文件上傳的第三方模塊,本站曾經(jīng)介紹過使用formidable模塊處理文件上傳,下面簡單介紹使用Node.js原生環(huán)境處理圖片上傳,上傳文件時也可以參考處理。
首先,使用Node.js的HTTP模塊創(chuàng)建一個HTTP服務(wù)器:
const http = require('http');
const fs = require('fs');
const util = require('util');
const querystring =require('querystring');
//用http模塊創(chuàng)建一個http服務(wù)端
http.createServer(function(req, res) {
if (req.url == '/upload' && req.method.toLowerCase() === 'get') {
//顯示一個用于文件上傳的form
res.writeHead(200, {'content-type': 'text/html'});
res.end(
'<form action="/upload" enctype="multipart/form-data" method="post">'+
'<input type="file" name="upload" multiple="multiple" />'+
'<input type="submit" value="Upload" />'+
'</form>'
);
} else if (req.url == '/upload' && req.method.toLowerCase() === 'post') {
if(req.headers['content-type'].indexOf('multipart/form-data')!==-1)
parseFile(req, res)
} else {
res.end('其它提交方式');
}
}).listen(3000);
在這一步中,我們創(chuàng)建HTTP 服務(wù)器,當(dāng)GET請求時,會加載一上用于文件上傳的form表單。上傳文件會通過POST方式提交到服務(wù)器,這時服務(wù)端會通過parseFile函數(shù)解析并保存文件,其解析代碼如下:
function parseFile (req, res) {
req.setEncoding('binary');
var body = ''; // 文件數(shù)據(jù)
var fileName = ''; // 文件名
// 邊界字符串
var boundary = req.headers['content-type'].split('; ')[1].replace('boundary=','');
req.on('data', function(chunk){
body += chunk;
});
req.on('end', function() {
var file = querystring.parse(body, '\r\n', ':')
// 只處理圖片文件
if (file['Content-Type'].indexOf("image") !== -1)
{
//獲取文件名
var fileInfo = file['Content-Disposition'].split('; ');
for (value in fileInfo){
if (fileInfo[value].indexOf("filename=") != -1){
fileName = fileInfo[value].substring(10, fileInfo[value].length-1);
if (fileName.indexOf('\\') != -1){
fileName = fileName.substring(fileName.lastIndexOf('\\')+1);
}
console.log("文件名: " + fileName);
}
}
// 獲取圖片類型(如:image/gif 或 image/png))
var entireData = body.toString();
var contentTypeRegex = /Content-Type: image\/.*/;
contentType = file['Content-Type'].substring(1);
//獲取文件二進(jìn)制數(shù)據(jù)開始位置,即contentType的結(jié)尾
var upperBoundary = entireData.indexOf(contentType) + contentType.length;
var shorterData = entireData.substring(upperBoundary);
// 替換開始位置的空格
var binaryDataAlmost = shorterData.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
// 去除數(shù)據(jù)末尾的額外數(shù)據(jù),即: "--"+ boundary + "--"
var binaryData = binaryDataAlmost.substring(0, binaryDataAlmost.indexOf('--'+boundary+'--'));
// 保存文件
fs.writeFile(fileName, binaryData, 'binary', function(err) {
res.end('圖片上傳完成');
});
} else {
res.end('只能上傳圖片文件');
}
});
}
req是一個IncomingMessage對象,而該對象又實(shí)現(xiàn)了ReadableStream,所以我們可以用流的方式來接收數(shù)據(jù)。數(shù)據(jù)接收完成了,按rfc1867規(guī)范進(jìn)行了數(shù)據(jù)處理,并通過fs模塊保存了文件。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用koa2創(chuàng)建web項(xiàng)目的方法步驟
這篇文章主要介紹了使用koa2創(chuàng)建web項(xiàng)目的方法步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-03-03
使用node.js半年來總結(jié)的 10 條經(jīng)驗(yàn)
從3月初來到帝都某創(chuàng)業(yè)公司的服務(wù)器團(tuán)隊(duì)實(shí)習(xí),到現(xiàn)在已接近半年的時間。PS: 已轉(zhuǎn)正,服務(wù)器端用的 Node。2014-08-08
node.js應(yīng)用后臺守護(hù)進(jìn)程管理器Forever安裝和使用實(shí)例
這篇文章主要介紹了node.js應(yīng)用后臺守護(hù)進(jìn)程管理器Forever安裝和使用實(shí)例,forever可以看做是一個nodejs的守護(hù)進(jìn)程,能夠啟動,停止,重啟我們的app應(yīng)用,需要的朋友可以參考下2014-06-06
Nodejs小文件拷貝復(fù)制和大文件拷貝復(fù)制方法代碼
NodeJS提供了基本的文件操作API,但是像文件拷貝復(fù)制這種高級功能就沒有提供,因此我們先拿文件拷貝程序練手,文件拷貝復(fù)制是在Node.js中常見的操作之一,它允許我們將一個文件的內(nèi)容復(fù)制到另一個文件中2023-11-11

