預設 PHP 限制上傳 2MB,實務中常需上傳數百 MB 甚至 GB 級檔案。需協調調整以下四組關鍵參數:
| 參數 | 預設值 | 建議大值 | 說明 |
|---|---|---|---|
file_uploads | On | On | 啟用 HTTP 檔案上傳 |
upload_max_filesize | 2M | 100M~2G | 單一檔案最大值 |
post_max_size | 8M | > upload_max_filesize | POST 總資料大小,需大於檔案上限 |
upload_tmp_dir | 系統預設 | /var/tmp/uploads | 暫存目錄,需可寫、空間足夠、非 Web 可讀 |
大檔案上傳耗時久,必須放寬執行時間與記憶體限制:
| 參數 | 預設值 | 建議值 | 影響 |
|---|---|---|---|
max_execution_time | 30 | 0 (無限制) 或 3600 | 腳本最大執行秒數 |
max_input_time | 60 | 0 或 3600 | 解析輸入資料最大秒數 (含上傳) |
memory_limit | 128M | 256M~512M | 腳本記憶體上限,分塊處理可較低 |
http {
client_max_body_size 2G; # 客戶端請求體最大值
client_body_timeout 300s; # 讀取請求體超時
proxy_read_timeout 300s; # 代理讀取超時
proxy_send_timeout 300s; # 代理發送超時
# 快取區域 (選用)
client_body_temp_path /var/tmp/nginx/client_body 1 2;
}
LimitRequestBody 2147483648 # 2GB (bytes)
TimeOut 300
# mod_security 可能需調整
SecRequestBodyLimit 2147483648
SecRequestBodyInMemoryLimit 1048576
function handleLargeUpload($tmpPath, $destPath, $chunkSize = 1024 * 1024) {
$src = fopen($tmpPath, 'rb');
$dst = fopen($destPath, 'wb');
while (!feof($src)) {
fwrite($dst, fread($src, $chunkSize));
}
fclose($src);
fclose($dst);
return true;
}
// 前端切片 (如使用 Uppy, Resumable.js, Dropzone)
// 後端合併端點
function mergeChunks($uploadId, $totalChunks, $finalName) {
$chunkDir = "/tmp/chunks/$uploadId/";
$finalPath = "/uploads/$finalName";
$fp = fopen($finalPath, 'wb');
for ($i = 1; $i <= $totalChunks; $i++) {
$chunkFile = $chunkDir . $i;
if (!file_exists($chunkFile)) return false;
fwrite($fp, file_get_contents($chunkFile));
unlink($chunkFile); // 釋放空間
}
fclose($fp);
rmdir($chunkDir);
return true;
}
// 使用 php://input 串流 (需搭配前端直接 POST 二進位)
// 注意:此模式下 $_FILES 不會自動填入
$input = fopen('php://input', 'rb');
$hash = hash_init('sha256');
$size = 0;
while (!feof($input)) {
$chunk = fread($input, 8192);
hash_update($hash, $chunk);
$size += strlen($chunk);
if ($size > MAX_SIZE) throw new Exception('超過大小限制');
// 可即時寫入目的地或驗證 MIME (需緩衝頭部)
}
fclose($input);
| 現象 | 可能原因 | 解決方向 |
|---|---|---|
| 上傳無反應、白頁 | post_max_size 過小 | 調大 post_max_size > upload_max_filesize |
| $_FILES 為空 | 表單缺 enctype、或 Nginx client_max_body_size 過小 | 檢查表單屬性、Nginx 配置 |
| 上傳中斷 (504/502) | proxy_read_timeout 過短 | 增加 Nginx/Apache 超時設定 |
| 暫存目錄空間不足 | upload_tmp_dir 分區太小 | 掛載大磁碟、或改用串流直寫 |
| 記憶體耗盡 | memory_limit 過小、或未分塊處理 | 分塊讀寫、避免 file_get_contents 大檔案 |
大檔案上傳非單一參數調整即可解決,需從 PHP、Web Server、網路基礎設施、前端體驗、後端架構全鏈路優化。現代專案強烈建議採用雲端直傳 + 分片上傳 + 非同步處理架構,將上傳壓力從應用層剝離。