基於node實現文件上傳和下載

本文主要講述基於node如何實現文件上傳和下載,分成原生node實現版、中間件實現版。

1、文件上傳爲什麼需要使用multipart/form-data編碼類型?

The encoding type application/x-www-form-urlencoded is inefficient for sending large quantities of binary data or text containing non-ASCII characters. Thus, a new media type,multipart/form-data, is proposed as a way of efficiently sending the values associated with a filled-out form from client to server.

文檔大概意思是說application/x-www-form-urlencoded不適合用於傳輸大型二進制數據和包含非ASCII字符的文本,因此提出了一個新的MIME類型multipart/form-data,有效地將與填寫的表單相關聯的值從客戶端發送到服務器。想了解很多MIME類型,點擊查看

2、文件對象介紹

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</head>
<body>
  <form method="POST" id="uploadForm" enctype="multipart/form-data">
    <input type="file" id="file" name="file" />
  </form>
  
  <button id="submit">submit</button>
</body>
</html>
<script>
  $("#submit").click(function() {
      console.log($("#file")[0].files[0])
  });
</script>

image.png

文件(File)接口提供有關文件的信息,並允許網頁中的 JavaScript 訪問其內容。

File 對象是特殊類型的 Blob,且可以用在任意的 Blob 類型的 context 中。比如說, FileReader, URL.createObjectURL(), createImageBitmap(), 及 XMLHttpRequest.send() 都能處理 Blob 和 File。

另外還有一個blob對象,也附上一張chrome瀏覽器的截圖。blob,二進制大文檔存儲

兩個對象裏面都有size和type,按照官方的文檔,file集成了blob,而且可以使用slice方法.

slice方法可以在當前的blob數據中,取出一段數據,作爲新的blob。常用的就是文件斷點上傳。

3、node中的buffer、Stream、fs模塊介紹

JavaScript語言沒有用於讀取或處理二進制數據流的機制。

但在處理像TCP流或文件流時,必須使用到二進制數據。因此在 Node.js中,定義了一個 Buffer 類,該類用來創建一個專門存放二進制數據的緩存區。

在 Node.js 中,Buffer 類是隨 Node 內核一起發佈的核心庫。Buffer 庫爲 Node.js 帶來了一種存儲原始數據的方法,可以讓 Node.js 處理二進制數據,每當需要在 Node.js 中處理I/O操作中移動的數據時,就有可能使用 Buffer 庫。原始數據存儲在 Buffer 類的實例中。一個 Buffer 類似於一個整數數組,但它對應於 V8 堆內存之外的一塊原始內存(buffer 是 C++ 層面分配的,所得內存不在 V8 內)

const Buffer = require('buffer').Buffer
var buf1 = Buffer.from('tést');
var buf2 = Buffer.from('tést', 'latin1');
var buf3 = Buffer.from([1, 2, 3]);
console.log(buf1, buf2, buf3);

輸出的結果爲:

<Buffer 74 c3 a9 73 74> <Buffer 74 e9 73 74> <Buffer 01 02 03>

不是說node引入buffer是來處理二進制數據流嗎?怎麼轉換成buffer對象打印出來卻不是二進制,而是十六進制呢?

在計算機內使用二進制表示數據,一個存儲空間叫做一個 bit ,只能存儲 0 或是 1。 通常,計算機把 8 個bit作爲一個存儲的單位,稱爲一個 Byte。
於是一個 Byte 可以出現 256 種不同的情況。

一個 Buffer 是一段內存,比如大小爲 2(Byte)的buffer,一共有 16 bit ,比如是00000001 00100011,可是這樣顯示太不方便。所以顯示這段內存的數據的時候,用其對應的 16 進制就比較方便了,是01 23,之所以用 16 進制是因爲轉換比較方便。

內存僅僅存儲的是二進制的數據,但是如何解釋就是我們人類自己的事情了。。。。比如A在 內存中佔用兩個Byte,對應的內存狀態是0000000 01000001,而uint16(JS不存在這個類型) 類型的65對應的存儲內存的狀態也是這個。

如果輸出 Buffer 那麼nodejs 輸出的是內存實際存儲的值(因爲你沒有給出如何解釋這段內存中的數據),可是二進制顯示起來不方便看,所以轉換爲 16 進制方便人類閱讀。
如果轉換爲數組,那麼意思就是把這個 buffer 的每一個字節解釋爲一個數字(其實是10進制數字,這是人類最方便的),所以是 0~255 的 10 進制數字。
總之,這樣轉化的目的是方便顯示和查看。

Stream是一個抽象接口,Node中有很多對象實現了這個接口。例如,對http服務器發起請求的request對象和服務端響應對象response就是Stream,還有stdout(標準輸出)。

你可以把流理解成一種傳輸的能力。通過流,可以以平緩的方式,無副作用的將數據傳輸到目的地。Stream表示的是一種傳輸能力,Buffer是傳輸內容的載體 (可以這樣理解,Stream:外賣小哥哥, Buffer:你的外賣)

在node中流無處不在:
image

流爲什麼這麼好用還這麼重要呢?

現在有個需求,我們要向客戶端傳輸一個大文件。每次接收一個請求,就要把這個大文件讀入內存,然後再傳輸給客戶端。

const fs = require('fs');
const server = require('http').createServer();

server.on('request', (req, res) => {
    fs.readFile('./big.file', (err, data) => {
        if (err) throw err;
        res.end(data);
    });
});

server.listen(8000);

通過這種方式可能會產生以下三種後果:

  • 內存耗盡
  • 拖慢其他進程
  • 增加垃圾回收器的負載

所以這種方式在傳輸大文件的情況下,不是一個好的方案。併發量一大,幾百個請求過來很容易就將內存耗盡。

如果採用流呢?

const fs = require('fs');
const server = require('http').createServer();

server.on('request', (req, res) => {
    const src = fs.createReadStream('./big.file');
    src.pipe(res);
});

server.listen(8000);

採用這種方式,不會佔用太多內存,可以將文件一點一點讀到內存中,再一點一點返回給用戶,讀一部分,寫一部分。(利用了 HTTP 協議的 Transfer-Encoding: chunked 分段傳輸特性),用戶體驗得到優化,同時對內存的開銷明顯下降。如果想在傳輸的過程中,想對文件進行處理,比如壓縮、加密等等,也很好擴展。

未完待續。。。

參考文獻:
buffer概述:
http://www.runoob.com/nodejs/nodejs-buffer.html
https://nodejs.org/api/buffer.html#buffer_buffer
buffer數據顯示:
https://segmentfault.com/q/1010000009002065

stream運行機制:
https://www.php.cn/js-tutorial-412138.html
stream的理解:
https://www.jianshu.com/p/4eb9077a8956

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章