2024 年 PHP Conference Japan

工作階段上傳進度

session.upload_progress.enabled INI 選項啟用時,PHP 將能夠追蹤個別上傳檔案的進度。此資訊對於實際的上傳請求本身並不是特別有用,但在檔案上傳期間,應用程式可以發送 POST 請求到一個獨立的端點(例如透過 XHR)來檢查狀態。

當上傳正在進行時,上傳進度會儲存在 $_SESSION 超級全域變數中,並且當 POST 的變數名稱與 INI 設定 session.upload_progress.name 相同時才會生效。當 PHP 偵測到這樣的 POST 請求時,它會在 $_SESSION 中填充一個陣列,其中索引值是 session.upload_progress.prefixsession.upload_progress.name 這兩個 INI 選項值的串接。通常藉由讀取這些 INI 設定來取得索引鍵,例如:

<?php
$key
= ini_get("session.upload_progress.prefix") . $_POST[ini_get("session.upload_progress.name")];
var_dump($_SESSION[$key]);
?>

也可以透過將 $_SESSION[$key]["cancel_upload"] 鍵值設為 true 來*取消*目前正在進行的檔案上傳。在同一個請求中上傳多個檔案時,這只會取消目前正在進行和待處理的檔案上傳,但不會移除已成功完成的上傳。當上傳以此方式取消時,$_FILES 陣列中的 error 鍵值將會被設為 UPLOAD_ERR_EXTENSION

INI 選項 session.upload_progress.freqsession.upload_progress.min-freq 控制重新計算上傳進度資訊的頻率。透過合理地設定這兩個選項,此功能的額外負擔幾乎可以忽略不計。

範例 #1 範例資訊

進度上傳陣列結構的範例。

<form action="upload.php" method="POST" enctype="multipart/form-data">
 <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />
 <input type="file" name="file1" />
 <input type="file" name="file2" />
 <input type="submit" />
</form>

儲存在 session 中的資料看起來會像這樣

<?php
$_SESSION
["upload_progress_123"] = array(
"start_time" => 1234567890, // 請求時間
"content_length" => 57343257, // POST 內容長度
"bytes_processed" => 453489, // 已接收並處理的位元組數量
"done" => false, // POST 處理程式完成時為 true,無論成功或失敗
"files" => array(
0 => array(
"field_name" => "file1", // <input/> 欄位的名稱
// 以下 3 個元素與 $_FILES 中的元素相同
"name" => "foo.avi",
"tmp_name" => "/tmp/phpxxxxxx",
"error" => 0,
"done" => true, // 當 POST 處理程式完成處理此檔案時為 true
"start_time" => 1234567890, // 此檔案開始處理的時間
"bytes_processed" => 57343250, // 此檔案已接收並處理的位元組數
),
// 同一個請求中的另一個檔案,尚未完成上傳
1 => array(
"field_name" => "file2",
"name" => "bar.avi",
"tmp_name" => NULL,
"error" => 0,
"done" => false,
"start_time" => 1234567899,
"bytes_processed" => 54554,
),
)
);

警告

要讓此功能正常運作,必須停用網頁伺服器的請求緩衝,否則 PHP 可能只會在檔案完全上傳後才看到上傳動作。已知 Nginx 等伺服器會緩衝較大的請求。

注意

上傳進度資訊會在任何腳本執行之前寫入到工作階段。因此,透過 ini_set()session_name() 更改工作階段名稱將導致工作階段中沒有上傳進度資訊。

新增註記

使用者貢獻的註記 11 則註記

165
s.zarges
12 年前
請注意,當您的網頁伺服器透過 FastCGI 執行 PHP 時,此功能將無法運作。工作階段陣列中不會有任何進度資訊。
很遺憾,PHP 只會在上傳完成後才取得資料,因此無法顯示任何進度。

希望這些資訊對您有所幫助。
63
howtomakeaturn
9 年前
注意事項

在表單中,請將上傳進度工作階段名稱的輸入欄位放置在檔案欄位**之前**

<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />
<input type="file" name="file1" />
<input type="file" name="file2" />
<input type="submit" />
</form>

如果您將它放在檔案欄位之後,您將會浪費很多時間找出原因(就像我一樣...)

以下的表單會讓您抓狂,而且真的會浪費很多時間

<form action="upload.php" method="POST" enctype="multipart/form-data">
<input type="file" name="file1" />
<input type="file" name="file2" />
<input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="123" />
<input type="submit" />
</form>

**不要**這樣做!
24
匿名
11 年前
雖然文件中提供的範例是正確的,但說明有點偏差。 為了釐清:

PHP 會在 $_SESSION 中填入一個陣列,其中索引是 session.upload_progress.prefix 和 POST 的 session.upload_progress.name 變數值的串連值。
14
isius
12 年前
如果您看到
「PHP 警告:未知:工作階段 ID 過長或包含非法字元,有效字元為 a-z、A-Z、0-9 和「-」,位於未知的第 0 行」,
那麼放錯位置的輸入欄位可能是原因。值得再次一提的是,隱藏元素**必須**放在檔案元素之前。
6
jortsc at gmail dot com
11 年前
請注意,如果您執行該程式碼並印出 $_SESSION[$key] 的內容,您會得到一個空陣列,因為 session.upload_progress.cleanup 預設為開啟,它會在讀取所有 POST 資料後立即清除進度資訊。

將其設定為 Off 或 0 即可查看 $_SESSION[$key] 的內容。
5
nihaopaul at gmail dot com
12 年前
需要注意的是,隱藏元素必須在檔案元素之前,否則您將無法獲得任何更新。
3
匿名
11 年前
別忘了,在產生表單之前必須先初始化 session,否則上述範例將無法運作。
1
alice at librelamp dot com
8 年前
實作這個功能時,我有兩個地方卡住了。

第一個 - 如果您使用 session_name() 來更改 session 的名稱,這將會失效。我是透過查看 phpinfo() 並發現它顯示不同的 session 名稱才發現這一點的。

至少在 Apache 中,設定 session 的更好方法是在您的 apache 設定檔中使用

php_value session.name "您的自訂名稱"

它位於 Directory 指令內,可能也可以在 .htaccess 中使用 - 我不確定。

-=-

第二個 - 在 apache 中,不要使用 mod_mpm_prefork.so

這就是我遇到的問題,這是 CentOS 7 的預設設定。

問題是它會導致 Apache 等待任何其他請求,直到上傳完成。

將該模組註釋掉並改用 mod_mpm_worker.so 解決了這個問題,而且進度計也正常運作了。
0
raptor98 at email dot cz
2 個月前
警告
不要更改 session.save_path 和 session.name(在您的應用程式中)!

上傳資訊的請求必須是 POST,且具有與您的隱藏輸入 (session.upload_progress.name) 相同的值和名稱。

資訊
它可以在 IIS /FastCGI 下運作(在 PHP 5.4 和 PHP 8.2 下,其他版本未測試)。
1
StrateGeyti
10 年前
似乎如果您發送一個包含如下欄位的表單

<?php echo '<input type="hidden" name="'.ini_get('session.upload_progress.name') .'" value="123" />'; ?>

但沒有任何類型為 "file" 的欄位,伺服器將會回應 500 錯誤。
1
ricki at rocker dot com
9 年前
session.upload_progress 更新完全忽略透過 session_set_save_handler() 設定的自訂 session 處理程式
To Top