首頁 > web前端 > js教程 > 主體

Node.js實作檔案上傳

高洛峰
發布: 2016-12-24 17:23:20
原創
1525 人瀏覽過

在工作中碰到了這樣的需求,需要用nodejs 來上傳文件,之前也只是知道怎麼透過瀏覽器來上傳文件, 用nodejs的話, 相當於模擬瀏覽器的行為。 google 了一番之後, 明白了瀏覽器無非就是利用http協議來給伺服器傳輸數據, 具體協議就是《RFC 1867 - Form-b​​ased File Upload in HTML》, 在瀏覽器上透過form 表單來上傳文件就是透過這個協議,我們可以先看看瀏覽器給服務端發送了什麼數據, 就可以依葫蘆畫瓢的把上傳功能實現出來。說起form 表單上傳檔案的話, 大家應該很熟悉:

<form action="http://www.qq.com/" method="post">
<input type="text" name="text1" /><br />
<input type="text" name="text2" /><br />
<input type="submit" />
</form>
登入後複製

   


提交時, 用fiddler 抓包可以看到向服務端發出這樣的資料:
.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預設為application/x-www-form-urlencoded,所以訊息會經過URL編碼。例如「你好」會編碼為 %E4%BD%A0%E5%A5%BD。

接下來我們來看看透過form 表單是怎麼上傳的。大家應該也不陌生:

<form action="http://www.qq.com" method="post" enctype="multipart/form-data">
<input type="file" name="myfile" />
<input type="submit" value="submit" />
</form>
登入後複製

   

然後新建一個只有hello world字樣的upload.txt文字檔案上傳上去,我們再吃用fiddler 來抓下包, 可以發現發送過去的資料txt文字檔上傳上去,我們再吃用fiddler 來抓下包, 可以發現發送過去的資料時間稍微複雜了一些(已經去掉了很多的其它沒關係的請求行,例如快取控制和cookie之類的):


POST http://www.qq.com/ HTTP/1.1

Host: www.qq.com
Content- Length: 199

Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywr3X7sXBYQQ4ZF5G


------WebKitFormBoundarywr3X7sXBYQQ4ZF5G5p="p); txt"
Content-Type: text/plain

hello world


------WebKitFormBoundarywr3X7sXBYQQ4ZF5G--

根據RFC 1867的定義,我們需要產生一段邊界數據,這個地方不能出現它的數據這個可以自己定義, 在每個瀏覽器的生成演算法可能都不一樣, 上面的boundary就是分隔數據,生成了分隔數據之後, 就可以把分隔數據放在頭部的Content-Type裡面傳送給服務端,也就是上文的Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywr3X7sXBYQQ4ZF5G, 另外,上傳的內容,需要用分隔資料來分隔成若干個段,然後每段資料裡面都有檔案的文件名,還有上傳時候的name,服務端就是用這個name來接收文件,還有文件的類型Content-Type,在這個例子裡是text/plain,如果上傳的是png圖片就是image/png。文件類型的一個空行後就是所上傳的文件的內容,在這個例子裡也是為了容易理解所以上傳的是文本文件所以內容直接就能夠顯示出來,如果上傳的是圖片文件, 因為是二進位文件,fiddler就顯示的是亂碼。 文件的內容結束之後就是一個空白行再加上邊界資料。 

了解了發送格式的細節之後, 下一步就是使用nodejs來編程實現,簡單來講, 就是按照格式把資料發送給服務端就行了。

const http = require(&#39;http&#39;);
const fs = require(&#39;fs&#39;);
//post地址为本地服务的一个php,用于测试上传是否成功
var options = {
hostname: &#39;localhost&#39;,
port: 80,
path: &#39;/get.php&#39;,
method: &#39;POST&#39;
}
//生成分隔数据
var boundaryKey = &#39;----WebKitFormBoundaryjLVkbqXtIi0YGpaB&#39;;
//读取需要上传的文件内容
fs.readFile(&#39;./upload.txt&#39;, function (err, data) {
//拼装分隔数据段
var payload = &#39;--&#39; + boundaryKey + &#39;\r\n&#39; + &#39;Content-Disposition:form-data; name="myfile"; filename="upload.txt"\r\n&#39; + &#39;Content-Type:text/plain\r\n\r\n&#39;;
payload += data;
payload += &#39;\r\n--&#39; + boundaryKey + &#39;--&#39;;
//发送请求
var req = http.request(options, function (res) {
res.setEncoding(&#39;utf8&#39;);
res.on(&#39;data&#39;, function (chunk) {
console.log(&#39;body:&#39; + chunk);
});
});
req.on(&#39;error&#39;, function(e) {
console.error("error:"+e);
});
//把boundary、要发送的数据大小以及数据本身写进请求
req.setHeader(&#39;Content-Type&#39;, &#39;multipart/form-data; boundary=&#39;+boundaryKey+&#39;&#39;);
req.setHeader(&#39;Content-Length&#39;, Buffer.byteLength(payload, &#39;utf8&#39;));
req.write(payload);
req.end();
});
登入後複製

   

本文重點在於了解協定並且用程式碼實作出來, 程式碼組織上面還有很多最佳化的地方。


最後在本地apache,簡單寫一個php來保存上傳的文件來用作測試:

<?php
$filePath = &#39;./upload.txt&#39;;
move_uploaded_file($_FILES[&#39;myfile&#39;][&#39;tmp_name&#39;] , $filePath);
echo "ok";
?>
登入後複製

   

另外,根據RFC 1867 還可以實現一次上傳多個文件的功能,在這個上傳多個文件不詳述, 需要的話可以詳細參考RFC 1867來實現。


以上所述是小編給大家介紹的Node.js實作檔案上傳,希望對大家有幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對PHP中文網的支持!


更多Node.js實作檔案上傳相關文章請關注PHP中文網!


相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!