首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 文件上傳兩種簡單實現:表單提交、多文件、錯誤處理、安全基礎

     編輯:PHP 相關知識

方案一:傳統單文件表單上傳(同步刷新頁面)

<?php
// upload_simple.php
$uploadDir = __DIR__ . '/uploads';
$maxSize = 2 * 1024 * 1024; // 2MB
$allowedExt = ['jpg','jpeg','png','gif','pdf','txt'];

if (!is_dir($uploadDir)) @mkdir($uploadDir, 0755, true);

$msg = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!isset($_FILES['file']) || $_FILES['file']['error'] !== UPLOAD_ERR_OK) {
        $msg = '上傳失敗: ' . ($_FILES['file']['error'] ?? '未知錯誤');
    } else {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
        $ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
        if (!in_array($ext, $allowedExt, true)) {
            $msg = '不允許的文件類型';
        } elseif ($_FILES['file']['size'] > $maxSize) {
            $msg = '文件過大(限制 2MB)';
        } else {
            $newName = bin2hex(random_bytes(8)) . '.' . $ext;
            $dest = $uploadDir . DIRECTORY_SEPARATOR . $newName;
            if (move_uploaded_file($_FILES['file']['tmp_name'], $dest)) {
                $msg = "上傳成功!文件: $newName";
            } else {
                $msg = '文件移動失敗,請檢查目錄權限';
            }
        }
    }
}
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>簡單上傳</title></head><body>
<h1>文件上傳(單文件)</h1>
<p style="color:green"><?= $msg ?></p>
<form method="post" enctype="multipart/form-data">
    <input type="hidden" name="MAX_FILE_SIZE" value="<?= $maxSize ?>">
    <input type="file" name="file" required>
    <button type="submit">上傳</button>
</form>
</body></html>

方案二:多文件上傳(HTML5 multiple 屬性)

<?php
// upload_multi.php
$uploadDir = __DIR__ . '/uploads';
$maxSize = 5 * 1024 * 1024;
$allowedMimes = ['image/jpeg','image/png','image/gif','image/webp','application/pdf'];

if (!is_dir($uploadDir)) @mkdir($uploadDir, 0755, true);

$results = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['files'])) {
    $files = $_FILES['files'];
    $count = count($files['name']);
    for ($i = 0; $i < $count; $i++) {
        $err = $files['error'][$i];
        if ($err !== UPLOAD_ERR_OK) {
            $results[] = ['name' => $files['name'][$i], 'ok' => false, 'msg' => "錯誤代碼: $err"];
            continue;
        }
        $tmp = $files['tmp_name'][$i];
        $size = $files['size'][$i];
        $name = $files['name'][$i];

        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $tmp);
        $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));

        if (!in_array($mime, $allowedMimes, true)) {
            $results[] = ['name' => $name, 'ok' => false, 'msg' => "類型不允許: $mime"];
            continue;
        }
        if ($size > $maxSize) {
            $results[] = ['name' => $name, 'ok' => false, 'msg' => '文件過大'];
            continue;
        }

        $newName = bin2hex(random_bytes(10)) . '.' . $ext;
        $dest = $uploadDir . DIRECTORY_SEPARATOR . $newName;
        if (move_uploaded_file($tmp, $dest)) {
            $results[] = ['name' => $name, 'ok' => true, 'saved' => $newName, 'url' => "/uploads/$newName"];
        } else {
            $results[] = ['name' => $name, 'ok' => false, 'msg' => '保存失敗'];
        }
    }
}
?>
<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>多文件上傳</title></head><body>
<h1>多文件上傳</h1>
<form method="post" enctype="multipart/form-data">
    <input type="file" name="files[]" multiple required>
    <button type="submit">上傳選中文件</button>
</form>
<?php if ($results): ?>
<h2>結果</h2>
<ul>
<?php foreach ($results as $r): ?>
    <li style="color:<?= $r['ok']?'green':'red' ?>">
        <?= htmlspecialchars($r['name']) ?>: <?= $r['msg'] ?? '成功' ?>
        <?php if ($r['ok']): ?> - <a href="<?= $r['url'] ?>" target="_blank">查看</a> <?php endif; ?>
    </li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</body></html>

安全基礎檢查清單(兩種方案通用)

  1. MIME 驗證用 finfo,不可信任 $_FILES['type']。
  2. 隨機文件名(bin2hex(random_bytes())),防覆蓋、防猜測、防注入。
  3. 上傳目錄禁用腳本執行:Nginx location /uploads { php_flag engine off; }、Apache SetHandler default-handler。
  4. 限制大小:PHP upload_max_filesize、post_max_size、Nginx client_max_body_size、HTML MAX_FILE_SIZE 四層。
  5. 錯誤碼處理:完整覆蓋 UPLOAD_ERR_* 常量。
  6. 臨時文件清理:PHP 請求結束自動刪除 $_FILES['tmp_name'],但異常終止時可能殘留,建議定時清理 sys_get_temp_dir()。

進階:AJAX 無刷新上傳(Fetch + FormData)

<script>
async function uploadFiles(files) {
    const fd = new FormData();
    [...files].forEach(f => fd.append('files[]', f));
    const res = await fetch('upload_multi.php', {method: 'POST', body: fd});
    const html = await res.text();
    // 簡單演示:直接替換 body(實際應解析 JSON 更新 DOM)
    document.body.innerHTML = html;
}
document.querySelector('input[type=file]').addEventListener('change', e => {
    if (e.target.files.length) uploadFiles(e.target.files);
});
</script>

總結

本文提供了兩個「開箱即用」的 PHP 文件上傳示例:單文件同步表單、多文件 HTML5 表單。代碼包含 MIME 白名單驗證、隨機命名、大小限制、錯誤碼處理、目錄權限 等安全基線。實際項目中請根據業務體量擴展:分片斷點續傳、進度條、圖片壓縮/水印、病毒掃描、異步隊列、對象存儲直傳。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved