PHP 執行模型決定了腳本只有在檔案完全寫入暫存目錄後才會啟動。此時 $_FILES 已包含完整資訊,但「上傳過程」已結束。要在上傳「過程中」獲取進度,必須繞過這個限制,透過擴展模組在暫存階段記錄進度資料,供另一請求讀取。
# 安裝 (舊版 PHP)
pecl install apc
; php.ini
apc.enabled=1
apc.rfc1867=1 ; 關鍵:啟用 RFC1867 上傳進度追蹤
apc.rfc1867_prefix="upload_"
apc.rfc1867_name="APC_UPLOAD_PROGRESS"
apc.rfc1867_freq="0" ; 更新頻率 0=盡可能頻繁
apc.rfc1867_ttl=3600 ; 進度資料保留秒數
<form method="POST" enctype="multipart/form-data" action="upload.php">
<input type="hidden" name="APC_UPLOAD_PROGRESS" value="<?php echo uniqid(); ?>">
<input type="file" name="file">
<input type="submit">
</form>
<div id="progress">0%</div>
<script>
// 定時輪詢進度
setInterval(() => {
fetch('progress.php?key=' + document.querySelector('[name=APC_UPLOAD_PROGRESS]').value)
.then(r => r.json())
.then(data => { if(data) document.getElementById('progress').textContent = Math.round((data.current/data.total)*100) + '%'; });
}, 500);
</script>
<?php
if (isset($_GET['key'])) {
$status = apc_fetch('upload_' . $_GET['key']);
header('Content-Type: application/json');
echo json_encode($status ?: ['current'=>0, 'total'=>0]);
exit;
}
// 正常上傳處理邏輯...
?>
pecl install uploadprogress
; php.ini
extension=uploadprogress.so
uploadprogress.file.filename_template="/tmp/upd_%s.txt"
; 可選:uploadprogress.get_contents=1
<form method="POST" enctype="multipart/form-data" action="upload.php">
<input type="hidden" name="UPLOAD_IDENTIFIER" value="<?php echo uniqid(); ?>">
<input type="file" name="upfile">
<input type="submit">
</form>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// 正常上傳處理
if (is_uploaded_file($_FILES['upfile']['tmp_name'])) {
$uploadDir = 'uploads/';
$ext = strrchr($_FILES['upfile']['name'], '.');
$sessId = $_POST['UPLOAD_IDENTIFIER'];
$target = $uploadDir . $sessId . $ext;
move_uploaded_file($_FILES['upfile']['tmp_name'], $target);
echo '上傳完成';
}
exit;
}
if (!empty($_GET['sessid'])) {
// 進度查詢
header('Content-Type: application/json');
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
$info = uploadprogress_get_info($_GET['sessid']);
echo is_array($info) ? json_encode($info) : json_encode(['error'=>'not found']);
exit;
}
// 顯示表單頁面...
?>
| 特性 | APC | uploadprogress |
|---|---|---|
| PHP 版本相容 | PHP 5.4 前 | PHP 5.3 - 8.x |
| 安裝難度 | 較複雜 | pecl 安裝簡單 |
| 效能 | 共用記憶體快取 | 檔案儲存,稍慢 |
| 維護狀態 | 已棄用,被 OPcache 取代 | 較活躍維護 |
現代專案應棄用伺服器端擴展方案,改用:
XMLHttpRequest.upload.onprogress 或 fetch + ReadableStream 即時顯示擴展方案適合維護無法升級前端的舊系統;新專案請採用原生瀏覽器 API 架構。