Node.js實(shí)現(xiàn)文件上傳
在工作中碰到了這樣的需求,需要用nodejs 來(lái)上傳文件,之前也只是知道怎么通過(guò)瀏覽器來(lái)上傳文件, 用nodejs的話, 相當(dāng)于模擬瀏覽器的行為。 google 了一番之后, 明白了瀏覽器無(wú)非就是利用http協(xié)議來(lái)給服務(wù)器傳輸數(shù)據(jù), 具體協(xié)議就是《RFC 1867 - Form-based File Upload in HTML》, 在瀏覽器上通過(guò)form 表單來(lái)上傳文件就是通過(guò)這個(gè)協(xié)議,我們可以先看看瀏覽器給服務(wù)端發(fā)送了什么數(shù)據(jù), 就可以依葫蘆畫瓢的把上傳功能實(shí)現(xiàn)出來(lái)。說(shuō)起form 表單上傳文件的話, 大家應(yīng)該很熟悉:
<form action="http://www.qq.com/" method="post"> <input type="text" name="text1" /><br /> <input type="text" name="text2" /><br /> <input type="submit" /> </form>
提交時(shí), 用fiddler 抓包可以看到向服務(wù)端發(fā)出這樣的數(shù)據(jù):
POST http://www.qq.com/ HTTP/1.1
Host: www.qq.com
Content-Length: 23
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
text1=hello&text2=world
值得注意的是Content-Type默認(rèn)為application/x-www-form-urlencoded,所以消息會(huì)經(jīng)過(guò)URL編碼。比如“你好”會(huì)編碼為 %E4%BD%A0%E5%A5%BD。
接下來(lái)我們看一下通過(guò)form 表單是怎么上傳的。大家應(yīng)該也不陌生:
<form action="http://www.qq.com" method="post" enctype="multipart/form-data"> <input type="file" name="myfile" /> <input type="submit" value="submit" /> </form>
然后新建一個(gè)只有hello world字樣的upload.txt文本文件上傳上去,我們?cè)俪杂胒iddler 來(lái)抓下包, 可以發(fā)現(xiàn)發(fā)送過(guò)去的數(shù)據(jù)稍微復(fù)雜了一些(已經(jīng)去掉了很多的其它沒(méi)關(guān)系的請(qǐng)求行,比如緩存控制和cookie之類的):
POST http://www.qq.com/ HTTP/1.1
Host: www.qq.com
Content-Length: 199
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywr3X7sXBYQQ4ZF5G
------WebKitFormBoundarywr3X7sXBYQQ4ZF5G
Content-Disposition: form-data; name="myfile"; filename="upload.txt"
Content-Type: text/plain
hello world
------WebKitFormBoundarywr3X7sXBYQQ4ZF5G--
根據(jù)RFC 1867的定義,我們需要生成一段邊界數(shù)據(jù),這個(gè)數(shù)據(jù)不能在內(nèi)容的其它地方出現(xiàn),這個(gè)可以自己定義, 在每個(gè)瀏覽器的生成算法可能都不一樣, 上面的boundary就是分隔數(shù)據(jù),生成了分隔數(shù)據(jù)之后, 就可以把分隔數(shù)據(jù)放在頭部的Content-Type里面?zhèn)魉徒o服務(wù)端, 也就是上文的 Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywr3X7sXBYQQ4ZF5G, 另外,上傳的內(nèi)容,需要用分隔數(shù)據(jù)來(lái)分隔成若干個(gè)段,然后每段數(shù)據(jù)里面都有文件的文件名,還有上傳時(shí)候的name,服務(wù)端就是用這個(gè)name來(lái)接收文件,還有文件的類型Content-Type,在這個(gè)例子里是 text/plain,如果上傳的是png圖片就是image/png。文件類型的一個(gè)空行后就是所上傳的文件的內(nèi)容,在這個(gè)例子里也是為了容易理解所以上傳的是文本文件所以內(nèi)容直接就能夠顯示出來(lái),如果上傳的是圖片文件, 因?yàn)槭嵌M(jìn)制文件,fiddler 就顯示的是亂碼。 文件的內(nèi)容結(jié)束之后就是一個(gè)空行再加上邊界數(shù)據(jù)。
了解了發(fā)送格式的細(xì)節(jié)之后, 下一步就是使用nodejs來(lái)編程實(shí)現(xiàn),簡(jiǎn)單來(lái)講, 就是按照格式把數(shù)據(jù)發(fā)送給服務(wù)端就行了。
const http = require('http'); const fs = require('fs'); //post地址為本地服務(wù)的一個(gè)php,用于測(cè)試上傳是否成功 var options = { hostname: 'localhost', port: 80, path: '/get.php', method: 'POST' } //生成分隔數(shù)據(jù) var boundaryKey = '----WebKitFormBoundaryjLVkbqXtIi0YGpaB'; //讀取需要上傳的文件內(nèi)容 fs.readFile('./upload.txt', function (err, data) { //拼裝分隔數(shù)據(jù)段 var payload = '--' + boundaryKey + '\r\n' + 'Content-Disposition:form-data; name="myfile"; filename="upload.txt"\r\n' + 'Content-Type:text/plain\r\n\r\n'; payload += data; payload += '\r\n--' + boundaryKey + '--'; //發(fā)送請(qǐng)求 var req = http.request(options, function (res) { res.setEncoding('utf8'); res.on('data', function (chunk) { console.log('body:' + chunk); }); }); req.on('error', function(e) { console.error("error:"+e); }); //把boundary、要發(fā)送的數(shù)據(jù)大小以及數(shù)據(jù)本身寫進(jìn)請(qǐng)求 req.setHeader('Content-Type', 'multipart/form-data; boundary='+boundaryKey+''); req.setHeader('Content-Length', Buffer.byteLength(payload, 'utf8')); req.write(payload); req.end(); });
本文重點(diǎn)在于了解協(xié)議并且用代碼實(shí)現(xiàn)出來(lái), 代碼組織上面還有很多優(yōu)化的地方。
最后在本地apache,簡(jiǎn)單寫一個(gè)php來(lái)保存上傳的文件來(lái)用作測(cè)試:
<?php $filePath = './upload.txt'; move_uploaded_file($_FILES['myfile']['tmp_name'] , $filePath); echo "ok"; ?>
另外,根據(jù)RFC 1867 還可以實(shí)現(xiàn)一次上傳多個(gè)文件的功能, 這個(gè)在這里就不詳述, 需要的話可以詳細(xì)參考RFC 1867來(lái)實(shí)現(xiàn)。
以上所述是小編給大家介紹的Node.js實(shí)現(xiàn)文件上傳,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
[將免費(fèi)進(jìn)行到底]在Amazon的一年免費(fèi)服務(wù)器上安裝Node.JS, NPM和OurJS博客
此文是介紹如何在Amazon的一年免費(fèi)計(jì)劃上安裝Node.JS, NPM還有搭建OurJS博客系統(tǒng)。,需要的朋友可以參考下2014-08-08關(guān)于express與koa的使用對(duì)比詳解
很多人都在問(wèn)到底該用Koa還是express,所以下面這篇文章就來(lái)給大家再次的對(duì)比了關(guān)于express與koa的相關(guān)資料,通過(guò)對(duì)比大家可以更好的進(jìn)行選擇,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-01-01用純Node.JS彈出Windows系統(tǒng)消息提示框?qū)嵗?MessageBox)
這篇文章主要介紹了用純Node.JS彈出Windows系統(tǒng)消息提示框?qū)嵗?MessageBox),非常具有實(shí)用價(jià)值,需要的朋友可以參考下2017-05-05Node.js實(shí)現(xiàn)簡(jiǎn)單的爬取的示例代碼
這篇文章主要介紹了Node.js實(shí)現(xiàn)簡(jiǎn)單的爬取的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06nodejs服務(wù)內(nèi)存泄露排查過(guò)程和優(yōu)化方法
在開(kāi)發(fā)和部署Node.js應(yīng)用程序時(shí),內(nèi)存泄露是一個(gè)常見(jiàn)的挑戰(zhàn),本文將探討如何對(duì)于一個(gè)陌生項(xiàng)目進(jìn)行內(nèi)存排查和優(yōu)化的方法,文章通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11node.js中的path.normalize方法使用說(shuō)明
這篇文章主要介紹了node.js中的path.normalize方法使用說(shuō)明,本文介紹了path.normalize的方法說(shuō)明、語(yǔ)法、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12詳解PNPM?Monorepo依賴項(xiàng)管理功能模擬實(shí)現(xiàn)
這篇文章主要介紹了PNPM?Monorepo依賴項(xiàng)管理功能模擬實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03npm install -g 遇到權(quán)限問(wèn)題解析
這篇文章主要為大家介紹了npm install -g 遇到權(quán)限問(wèn)題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06