大文件上傳失敗是 PHP 開發中高頻痛點,往往涉及 PHP 核心配置、Web 伺服器限制、客戶端表單設置三個層面。本文系統梳理完整排查鏈路,提供可直接套用的配置模板與原理解析。
編輯 php.ini,重點調整以下指令(生效需重啟 PHP-FPM/Apache):
| 指令 | 預設值 | 建議值 | 說明 |
|---|---|---|---|
| file_uploads | On | On | 總開關,必須開啟 |
| upload_tmp_dir | 系統臨時目錄 | /tmp/php_upload | 需確保目錄存在且 PHP 進程用戶可寫 |
| upload_max_filesize | 2M | 100M | 單個文件上限 |
| post_max_size | 8M | 120M | 必須 ≥ upload_max_filesize,含所有 POST 數據 |
| max_execution_time | 30 | 300 | 腳本最大執行秒數,大文件需更久 |
| max_input_time | 60 | 300 | 解析 POST 數據最大秒數 |
| memory_limit | 128M | 256M | 腳本記憶體上限,流式處理可不調高 |
; 關鍵配置示例
file_uploads = On
upload_tmp_dir = /var/www/tmp_upload
upload_max_filesize = 100M
post_max_size = 120M
max_execution_time = 300
max_input_time = 300
memory_limit = 256M
Nginx 預設限制請求體大小為 1M,超過直接返回 413 Request Entity Too Large。
主配置 nginx.conf 的 http、server、location 三層均可設定,就近原則生效。若使用 include domains/* 分離虛擬主機,請修改對應站點配置。
server {
listen 80;
server_name example.com;
client_max_body_size 100M; # 請求體上限
client_body_buffer_size 128k; # 緩衝區,超過寫入臨時文件
client_body_temp_path /var/www/nginx_body_temp; # 臨時目錄需可寫
location / {
proxy_pass https://backend;
# 反向代理場景下,此處也需設定,否則由 server 繼承
client_max_body_size 100M;
}
}
修改後執行 nginx -t && nginx -s reload 熱重載。
<form action="upload.php" method="post" enctype="multipart/form-data">
<!-- 必須設置 enctype -->
<input type="hidden" name="MAX_FILE_SIZE" value="104857600"> <!-- 100M,瀏覽器端建議值 -->
<input type="file" name="userfile">
<input type="submit" value="上傳">
</form>
注意:MAX_FILE_SIZE 僅是瀏覽器建議,惡意用戶可繞過,**服務端驗證才是最後防線**。
<?php
// upload.php
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'];
$maxSize = 100 * 1024 * 1024; // 100M
$uploadDir = __DIR__ . '/uploads/';
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['userfile'])) {
$file = $_FILES['userfile'];
// 1. 錯誤碼檢查
if ($file['error'] !== UPLOAD_ERR_OK) {
$msg = [
UPLOAD_ERR_INI_SIZE => '超過 php.ini upload_max_filesize',
UPLOAD_ERR_FORM_SIZE => '超過表單 MAX_FILE_SIZE',
UPLOAD_ERR_PARTIAL => '文件部分上傳',
UPLOAD_ERR_NO_FILE => '未選擇文件',
UPLOAD_ERR_NO_TMP_DIR => '缺少臨時目錄',
UPLOAD_ERR_CANT_WRITE => '寫入失敗',
];
die($msg[$file['error']] ?? '未知錯誤');
}
// 2. 類型與大小二次驗證
if (!in_array($file['type'], $allowedTypes)) die('類型不允許');
if ($file['size'] > $maxSize) die('文件過大');
// 3. 防止同名覆蓋:日期 + 隨機字串 + 原始副檔名
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$newName = date('YmdHis') . '_' . bin2hex(random_bytes(8)) . '.' . $ext;
$dest = $uploadDir . $newName;
// 4. 移動文件(驗證是否真為 HTTP 上傳)
if (!move_uploaded_file($file['tmp_name'], $dest)) {
die('移動失敗,請檢查目錄權限');
}
chmod($dest, 0644);
echo "上傳成功:$newName";
}
?>
超大文件(>500M)建議採用前端分片(如 WebUploader、Plupload)、後端合併策略,避免單次請求超時、記憶體飆升。PHP 端可使用 fopen('php://input') 流式寫入臨時文件,而非依賴 $_FILES 全量緩衝。
php.ini 三大尺寸參數已調大並重啟 PHPclient_max_body_size 已配置在正確 server/location 並 reloadenctype="multipart/form-data" 未遺漏df -h 確認)php-fpm.log、nginx/error.log)無 413/500/權限報錯按層級逐項排查,99% 的大文件上傳問題均可解決。若仍失敗,請檢查是否有 CDN/WAF/負載均衡器額外限制請求體大小。