上傳文件到服務器是個常見的需求,但是從瀏覽器到服務器中間經過一些網關,或服務器本身支持的請求内容長度有限,無法保證太大體積的文件上傳,於是需要將文件分片,切割成合理的體積來上傳到服務器,然後再進一步的處理。
Blob 是 JavaScript 中表示不可变、原始数据的类文件对象。只是對類文件對象的一種描述,並不包含真實的數據,儅讀取數據時需要使用 FileReader 對象或其他方法。
File 是特定類型的 Blob 對象。儅使用 input 標簽選擇文件時,即可返回此類對象。
其是 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
來分隔多個數據。
數據部分包含内容如下:
--boundary
:設置分割界限;Content-Disposition: form-data; name=
:設置數據為 form-data,名稱等;Content-Type
:數據的具體類型;最後,請求體將以 --boundary--
結束。
對於 Javascript 來説,通過 FormData 類可以很容易的創建出此種數據,可以包含 File 或 Blob 對象。因此接下來將使用此種數據格式來上傳文件。
其是 Java 中提供隨機訪問文件方法的類,可以在任意位置寫入特定的字節數目。
在上傳前,需要將超過一定體積的文件切割成小的分片,避免網路傳輸上的各種限制。由於 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.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 分片文件合并
儅後端可以隨機訪問文件位置,並不需要在磁盤上存儲分片文件,可以先創建個大文件,然後根據分片文件的偏移量來直接寫到大文件上即可。
當今網絡上的各種設備,服務器等都有著文件尺寸的限制,或大或小。儅需要實現文件上傳時,應該盡可能考慮到大文件的情況,要麽直接限制上傳尺寸,或如本文所説,實現大文件分片上傳機制,解決大文件的需求。