大文件分片上傳

零、前言

上傳文件到服務器是個常見的需求,但是從瀏覽器到服務器中間經過一些網關,或服務器本身支持的請求内容長度有限,無法保證太大體積的文件上傳,於是需要將文件分片,切割成合理的體積來上傳到服務器,然後再進一步的處理。

一、預備知識

1、Blob

Blob 是 JavaScript 中表示不可变、原始数据的类文件对象。只是對類文件對象的一種描述,並不包含真實的數據,儅讀取數據時需要使用 FileReader 對象或其他方法。

2、File

File 是特定類型的 Blob 對象。儅使用 input 標簽選擇文件時,即可返回此類對象。

3、multipart/form-data

其是 HTTP 中的一種包含多種數據格式的複合類型,通常儅 HTML 表單包含文件時,就會轉換為這種數據格式。一個請求體例子如下:

Content-Type: multipart/form-data; boundary=abcdefg # 请求体 --abcdefg Content-Disposition: form-data; name="name of file" Content-Type: application/octet-stream bytes of file --abcdefg Content-Disposition: form-data; name="key" Content-Type: text/plain;charset=UTF-8 text encoded in UTF-8 --abcdefg--

如上代碼所示,請求需要設置 Content-Type: multipart/form-data; boundary=abcdefg,其中 boundary 是必須設置的,不超過 70 個字符的分隔符。然後用 --boundary 來分隔多個數據。

數據部分包含内容如下:

  1. --boundary:設置分割界限;
  2. Content-Disposition: form-data; name=:設置數據為 form-data,名稱等;
  3. Content-Type:數據的具體類型;
  4. 空行
  5. 數據内容

最後,請求體將以 --boundary-- 結束。

對於 Javascript 來説,通過 FormData 類可以很容易的創建出此種數據,可以包含 File 或 Blob 對象。因此接下來將使用此種數據格式來上傳文件。

4、RandomAccessFile

其是 Java 中提供隨機訪問文件方法的類,可以在任意位置寫入特定的字節數目。

二、文件分片上傳

1、文件分片

在上傳前,需要將超過一定體積的文件切割成小的分片,避免網路傳輸上的各種限制。由於 Blob 類提供了 slice 方法,即可很輕鬆的切分成小分片文件對象。至於小分片文件大小的上限可根據實際情況設置,一般應該前後端設置相同的值。具體的切割方法如下:

function getChunks(file, size=1024*1024*10) { let chunks = []; for (let i = 0;i < file.size;i += size) { chunks.push({ "chunk": file.slice(i, i + size) }); } return chunks; }

分片后,可為每個分片文件設置一個文件名,通常的方法是計算文件的 md5 值當作文件名,這樣就可以上傳唯一的文件名。此外,還可以利用這種文件名,查看是否當前分片文件是否已經上傳過,從而實現續傳功能,加快傳輸速度。

2、上傳接口設計

總體上,文件上傳接口實現每個分片文件上傳完畢后,再將文件合而爲一,形成最終的文件。所以一般會有下列的接口形式:

2.1 單個文件分片上傳
使用 multipart/form-data 的數據類型來接收文件,參數可以設置如下:

名稱 參數名 類型
文件名 chunkFileName String
文件 file MultiPartFile
偏移量 offset Integer

偏移量可選,儅後端可以隨機讀寫文件位置時,即可在對應位置寫入文件。

2.2 文件合并
所有分片文件上傳完後,由前端發出合并請求,將文件合并到一起。可以設置的參數如下:

名稱 參數名 類型
文件名 fileName String
分片文件數組 chunkFiles Array<{fileName, offset}>

其中 offset 可以指示分片文件放的位置。

2.3 可能問題
2.3.1 文件名重複
可以先由後端生成唯一的文件名,再和上傳文件名關聯起來,磁盤上存儲的是唯一文件名,儅需要下載文件,再將上傳文件名賦值給下載文件。

2.3.2 分片文件合并
儅後端可以隨機訪問文件位置,並不需要在磁盤上存儲分片文件,可以先創建個大文件,然後根據分片文件的偏移量來直接寫到大文件上即可。

三、總結

當今網絡上的各種設備,服務器等都有著文件尺寸的限制,或大或小。儅需要實現文件上傳時,應該盡可能考慮到大文件的情況,要麽直接限制上傳尺寸,或如本文所説,實現大文件分片上傳機制,解決大文件的需求。