曾經嘗試過用PHP上傳大文件嗎?想知道您是否可以從上次中斷的地方繼續上傳,而不會在遇到任何中斷的情況下再次重新上傳整個數據?如果您覺得這個場景很熟悉,請接著往下閱讀。
文件上傳是我們幾乎所有現代Web項目中的一項很常見的任務。在任何語言中,有了可用的工具,實現文件上傳功能都不難。但是,對于大文件上傳,這個事情還是有些讓人頭疼。
假設您正在嘗試上傳相當大的文件。您已經等待了一個多小時,上傳率為90%。然后突然,您的連接斷開或瀏覽器崩潰。上傳被中止,您需要從頭開始上傳。這很令人沮喪,不是嗎?更糟糕的是,如果您的連接速度較慢,就像世界上許多地方一樣,無論嘗試多少次,每次都只能上傳第一部分內容。無論你重來多少次,你都不可能上傳成功。你的心態扛得住嘛?!?!

在這篇文章中,我們將嘗試通過使用tus協議以可恢復塊的形式上傳文件來解決PHP中的此問題。
首先什么是tus?
Tus是用于可恢復文件上傳的基于HTTP的開放協議。可恢復意味著可以在中斷的地方繼續工作,而不會在遇到任何中斷的情況下再次重新上傳整個數據。如果用戶希望暫停,則中斷可能會發生,或者在網絡問題或服務器中斷的情況下,偶然發生。
Vimeo于2017年5月采用了 Tus協議。
為什么是tus?
引用Vimeo的博客:
我們之所以決定在上載堆棧中使用tus,是因為tus協議以簡潔明了的方式標準化了上載文件的過程。這種標準化將使API開發人員可以將更多的精力放在其特定于應用程序的代碼上,而不必將精力放在上傳過程本身上。
通過這種方式上傳文件的另一個主要好處是,您可以從筆記本電腦開始上傳,甚至可以繼續從移動設備或任何其他設備上載相同的文件,這可以極大地提升用戶體驗。

入門
從添加我們的依賴關系開始
$ composer require ankitpokhrel/tus-php
tus-php是用于tus可斷續上傳協議v1.0.0的純PHP框架 服務器和客戶端實現。
tus-php-for用于tus可恢復上載協議v1.0.0的純PHP服務器和客戶端 github.com
更新:Vimeo現在在其官方PHP庫的v3 中將TusPHP 用于Vimeo API。
創建一個服務器來處理我們的請求
這就是簡單服務器的外觀。
// server.php$server = new TusPhpTusServer('redis');
$response = $server->serve();$response->send();exit(0); // 從當前的PHP進程退出.
您需要配置服務器以響應特定的端點。例如,在Nginx中,您可以執行以下操作:
# nginx.conflocation /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。您可以使用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 協議
核心協議
核心協議描述如何繼續中斷的上傳。這里假定你已經有一個用于上傳的 RUL ,這個 URL 通常是由擴展協議 Creation創建。
所有客戶端和服務端必須實現核心協議。
協議沒有描述 RUL 的結構,而是留給協議的實現來決定。本文中所有展示的 URL 僅用于舉例。
此外,認證和授權的實現也留給服務端來決定。
示例
用一個請求頭指明應當從什么地方開始續傳上傳。
以下示例展示中斷位置由70變為100
請求
HEAD /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: http://tus.example.org
Tus-Resumable: 1.0.0
響應
HTTP/1.1 200 OK
Upload-Offset: 70
Tus-Resumable: 1.0.0
對于給定的中斷位置,客戶端使用 PATCH 方法來續傳。
請求
PATCH /files/24e533e02ec3bc40c387f1a0e460e216 HTTP/1.1
Host: http://tus.example.org
Content-Type: application/offset+octet-stream
Content-Length: 30
Upload-Offset: 70
Tus-Resumable: 1.0.0
[remaining 30 bytes]
響應
HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
Upload-Offset: 100
由于 tus-php 項目 本身還出于初級階段,某些部分將來可能會有改動。在 example 文件夾里,有三個不同的例子供你參考。如果任何問題或者建議,歡迎留言交流。
Happy Coding!
更多學習內容請訪問從碼農成為架構師的修煉之路
以上內容希望幫助到大家,很多PHPer在進階的時候總會遇到一些問題和瓶頸,業務代碼寫多了沒有方向感,不知道該從那里入手去提升,對此我整理了一些資料,包括但不限于:分布式架構、高可擴展、高性能、高并發、服務器性能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階干貨需要的可以免費分享給大家,需要的可以加入我的官方群點擊此處。