原文地址:https://prinzeugen.net/implem...php
PHP 實現遠程下載文件到服務端並非什麼新鮮玩意,用 cURL
、file_get_contents
、fopen
等都可以輕易實現。前端
可是這幾種常規的方法都是在一個線程內下載文件,等文件下載完畢之後才能返回 HTTP 響應。所形成的結果就是用戶在頁面上點擊「下載到服務器」按鈕後,會看到空白頁和加載的小菊花轉啊轉,轉很久以後纔出現「下載成功」的頁面。git
固然,我上面所舉例的狀況是隻使用純粹的表單 POST 發送請求的狀況。如今的話就算再不濟也通常會使用 ajax
發送請求,而後在前臺放個加載動畫,等收到下載成功的迴應以後再進行下一步操做。github
可是!即便是去掉了噁心的且須要等待的空白頁,這樣作仍是對用戶體驗有很差的影響。沒有具體的下載進度,只有一個一直轉呀轉的菊花圖,估計挺多用戶都沒法坐和放寬吧(至少對於我來講是這樣的)ajax
而我一個 PHP 項目的一鍵更新系統正好須要重構,遂研究瞭如何在 PHP 做爲後端時顯示遠程文件下載進度條,並搗鼓出了個像樣的解決方案,在這裏分享一下。後端
也許你在搜索「PHP 下載 進度條」的時候會看到有些文章使用 PHP 的輸出控制函數(flush
之類的)控制緩衝區來實現進度條。可是——瀏覽器
那都是狗屁!緩存
沒有人能夠保證用戶的 PHP 關閉了默認開啓的 output buffering
,也沒法保證 瀏覽器 / Web Server 不對腳本輸出進行緩存。若是上述二者其中之一處於開啓狀態的話,你就會喜聞樂見的發現本應該慢慢增加的進度條會在等待完漫長的 xx 秒後一會兒蹦到 100%_(由於控制前端進度條長度的語句被緩存起來,在腳本執行結束後一併發送了,而不是一塊一塊地傳給瀏覽器)_。服務器
關於上面緩衝區控制的進度條就是辣雞的更多討論能夠查看文章底部的參考連接。架構
閒話休提。那麼咱們該如何實現下載進度條的更新呢?
首先經過後端一點點輸出控制進度條語句的方案已經 PASS 了,那麼咱們很天然的就會想到——
在前端設置一個定時器,Ajax 輪循下載進度並更新頁面上的進度條。
知道了原理以後,咱們先來考慮下總體的架構與步驟。
用戶點擊「下載」按鈕,前端展現出進度條,並 ajax 發送 prepare-download
的請求;
後端收到請求,進行遠程下載的準備工做 —— 準備好遠程文件連接、臨時文件存放位置以及文件的大小,並返回給瀏覽器;
前端拿到文件大小等信息後,發送真正的 start-download
請求(這個請求耗時可能會很長),並啓動輪循的計時器;
計時器啓動後,每隔一段時間發送 get-file-size
請求,獲取當前臨時文件的大小,計算進度後更新進度條;
直到下載完成。
下面給出先後端代碼的實例。
代碼照例放在 Gist 上,加載不出自行解決:
https://gist.github.com/print...
示例代碼使用了 ?action=xxx
的 Query String 形式來區分不一樣的指令,這些請酌情修改。和我業務邏輯有關的一些關鍵函數都被我替換爲淺顯易懂的名字(譬如 get_remote_file_url
)了,須要你本身去替換實現。
https://gist.github.com/print...
實例代碼用了 fopen
和循環 fwrite
寫入一個 chunk 的數據到臨時文件,這是借鑑了 KODExplorer 遠程下載的函數,在此致謝。另外也有經過 curl_setopt($ch, CURLOPT_FILE, $fp);
給 cURL 設置一個文件句柄的方法,可是我沒有測試成功,但願各位也能試一試。
以上。