PHP 中使用 TUS 協定來實現大檔案的斷點續傳

2020-07-16 10:05:43
你是否曾經為大檔案上傳而苦惱?如果檔案上傳的過程中,因為某種原因中斷了,是否可以從中斷的位置繼續上傳,而不用重新上傳整個檔案?如果你有這樣的困惑,那麼請繼續閱讀下面的內容。

在現代網站應用中,上傳檔案是非常常見的。在任何語言中,通過使用一些工具,都可以實現檔案上傳的功能。但是,如果處理大檔案上傳的需求,還是有點麻煩的。

假如你此時正在上傳一個很大的檔案,大約一個小時過去了,進度是 90%。突然斷網了或者瀏覽器崩潰了,上傳的程式退出,你要再全部重新來過。真的很不爽,對不對?還有更讓人鬱悶的是,如果你的網速很慢,那麼,無論你重來多少次,你都不可能上傳成功。

在 PHP 中,我們可以嘗試利用 tus 協定的斷點續傳功能來解決這個問題。

什麼是 tus?

Tus 是一個基於 HTTP 的 檔案斷點續傳開放協定。 斷點續傳的意思是不管是使用者自行中斷,還是由於網路等原因的意外中斷,都可以從中斷的地方繼續上傳,而不用重新開始。

Tus 協定是在 2017 年5月被 Vimeo 採用的。

為什麼用 tus?

參照 Vimeo 的部落格:

我們之所以決定用 tus,是因為它能以簡潔開放的形式,將檔案上傳的過程標準化。這種標準化有利於 API 的開發者更加專注於應用本身的邏輯,而非檔案上傳的過程。

使用這種方式上傳的另一個好處是,你可以在筆電上開始上傳檔案,然後又轉到手機或者其他裝置繼續上傳同一個檔案,這可以極大地提升使用者體驗。

cf9be286e856e57ab2dba8c46e23e2e.png

圖片: Tus 大致的工作流程

開始

第一步,載入依賴。

$ composer require ankitpokhrel/tus-php

tus-php 是用於 tus 斷點續傳協定 v1.0.0 的一個的純 PHP 框架,完美實現了 伺服器端與用戶端的互動 。

更新: 現在 Vimeo 官方 PHP 庫 的 v3 用的是 TusPHP

建立一個處理請求的伺服器端

你可以像下面這樣建立一個伺服器端.

// server.php
$server   = new TusPhpTusServer('redis');
$response = $server->serve();
$response->send();
exit(0); // 退出當前 PHP 進程

你需要設定你的伺服器以便能對特定的終端進行響應。如果使用 Nginx 的話你可以像下面這樣設定:

# nginx.conf
location /files {
    try_files $uri $uri/ /path/to/server.php?$query_string;
}

假設我們伺服器端的 URL 是 http://server.tus.local. 因此,基於我們上面的 Nginx 設定,我們可以通過 http://server.tus.local/files. 來存取到我們的 tus 終端.

基於 RESTful 風格的端點設定:

# 獲取有關伺服器目前設定的資訊
OPTIONS /files
# 檢查上傳的檔案是否合法
HEAD /files/{upload-key}
# 建立
POST /files
# 修改
PATCH /files/{upload-key}
# 刪除
DELETE /files/{upload-key}

檢視 協定細則 獲取更多關於路由的資訊。

如果你是用類似於 Laravel 的框架,那麼你就不需要在組態檔裡定義這些了, 可以直接定義路由來存取 tus 的基礎端點。我們會在另外的教學裡介紹相關細節。

使用 tus-php 用戶端處理上傳

伺服器到位後,用戶端可以塊的形式上傳檔案。 讓我們首先建立一個簡單的 HTML 表單來獲取使用者的輸入。

<form action="upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="tus_file" id="tus-file" />
    <input type="submit" value="Upload" />
</form>

提交表單後,我們需要按照幾個步驟來處理上傳。

建立一個 tus-php 用戶端物件

// Tus client
$client = new TusPhpTusClient('http://server.tus.local');

上面程式碼中的第一個引數是你的 tus 伺服器地址。

2. 使用檔案後設資料初始化用戶端

為了確保上傳檔案的唯一性,我們需要給每個上傳的檔案以唯一標識。這樣在檔案中斷後續傳的時候,伺服器就可以很清晰地辨識出,哪幾個片段是屬於同一個檔案得。這個標識碼可以自己指定,也可以由系統生成。

// 設定標識碼和檔案後設資料
$client->setKey($uploadKey)
    ->file($_FILES['tus_file']['tmp_name'], 'your file name');

如果不想指定標識碼,可以這樣寫,由系統會自動生成:

$client->file($_FILES['tus_file']['tmp_name'], 'your file name');
$uploadKey = $client->getKey(); // Unique upload key

3. 分塊上傳檔案

// $chunkSize 是以位元組為單位的,例如 5000000 等於 5 MB
$bytesUploaded = $client->upload($chunkSize);

當你想要續傳下一塊的時候,就可以帶上同樣的標識碼引數來續傳。

// 在下一個請求中續傳檔案
$bytesUploaded = $client->setKey($uploadKey)->upload($chunkSize);

檔案全部上傳完成後,預設情況下,伺服器會使用 sha256 來校驗檔案總和,以確保不會有丟失的檔案。

使用 tus-js-client 用戶端處理檔案上傳

tus 協定的團隊還開發了一個模組化的檔案上傳外掛 Uppy。這個外掛可以在官方 tus-js-client 和 tus-php 伺服器之間建立連線。也就是說我們可以使用 php 配合 js 來實現檔案上傳了。

uppy.use(Tus, {
  endpoint: 'https://server.tus.local/files/', // 你的 tus 伺服器
  resume: true,
  autoRetry: true,
  retryDelays: [0, 1000, 3000, 5000]
})

更多細節可以檢視 uppy 的文件, 這裡 還有些例子可以供你參考。

分塊上傳

tus-php 伺服器支援 concatenation 擴充套件,可以把多次上傳的檔案合為一個檔案。因此,我們可以在用戶端支援並行上傳以及非連續的分塊檔案上傳。

使用 tus-php 實現分塊上傳

tus-partial-upload.php

<?php
// 檔案唯一標識碼
$uploadKey = uniqid();
$client->setKey($uploadKey)->file('/path/to/file', 'chunk_a.ext');
// 從第 1000  個位元組開始上傳 10000 位元組
$bytesUploaded = $client->seek(1000)->upload(10000);
$chunkAkey     = $client->getKey();
// 從 第 0 個位元組開始上傳 10000 位元組
$bytesUploaded = $client->setFileName('chunk_b.ext')->seek(0)->upload(1000);
$chunkBkey     = $client->getKey();
// 從第 11000 個位元組  (10000 +  1000) 開始上傳剩餘的位元組
$bytesUploaded = $client->setFileName('chunk_c.ext')->seek(11000)->upload();
$chunkCkey     = $client->getKey();
// 把分塊上傳的檔案組合起來
$client->setFileName('actual_file.ext')->concat($uploadKey, $chunkAkey, $chunkBkey, $chunkCkey);

分塊上傳的完整例子 在這裡.

總結

由於 tus-php 專案 本身還出於初級階段,後面可能還會有一些改動。在 example 資料夾裡,有三個不同的例子供你參考。如果任何問題或者建議,歡迎留言交流。

以上就是PHP 中使用 TUS 協定來實現大檔案的斷點續傳的詳細內容,更多請關注TW511.COM其它相關文章!