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

PHP 文件上傳實戰:按日期自動創建目錄、類型校驗、縮略圖生成

     編輯:PHP 相關知識

完整上傳處理流程

<?php
session_start();
// 權限檢查略

// 配置
$config = [
    'allowTypes' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
    'maxSize'    => 5 * 1024 * 1024, // 5MB
    'uploadRoot' => __DIR__ . '/uploads',
    'thumb'      => true,
    'thumbSizes' => [
        'm' => [400, 400], // 中圖
        's' => [150, 150], // 小圖
    ],
    'watermark'  => __DIR__ . '/assets/watermark.png', // 可選
];

// 1. 按日期創建目錄
$dateDir = date('Ymd');
$targetDir = $config['uploadRoot'] . '/' . $dateDir;
if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true)) {
    exit(json_encode(['code' => 500, 'msg' => '目錄創建失敗']));
}

// 2. 檢查請求
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || empty($_FILES['file'])) {
    exit(json_encode(['code' => 400, 'msg' => '無效請求']));
}

$file = $_FILES['file'];

// 3. 錯誤碼檢查
if ($file['error'] !== UPLOAD_ERR_OK) {
    $errMsg = [
        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 => '寫入磁盤失敗',
        UPLOAD_ERR_EXTENSION  => '擴展阻止上傳',
    ];
    exit(json_encode(['code' => 400, 'msg' => $errMsg[$file['error']] ?? '上傳錯誤']));
}

// 4. 文件大小
if ($file['size'] > $config['maxSize']) {
    exit(json_encode(['code' => 400, 'msg' => '文件過大']));
}

// 5. MIME 類型二次驗證(防偽造 Content-Type)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
if (!in_array($mime, $config['allowTypes'], true)) {
    exit(json_encode(['code' => 400, 'msg' => '禁止的文件類型']));
}

// 6. 構建目標文件名
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$filename = bin2hex(random_bytes(8)) . '.' . strtolower($ext); // 隨機名防衝突
$destPath = $targetDir . '/' . $filename;

// 7. 移動文件
if (!move_uploaded_file($file['tmp_name'], $destPath)) {
    exit(json_encode(['code' => 500, 'msg' => '文件移動失敗']));
}

// 8. 生成縮略圖(需 GD/Imagick)
$thumbs = [];
if ($config['thumb'] && function_exists('imagecreatefromstring')) {
    $srcImg = imagecreatefromstring(file_get_contents($destPath));
    if ($srcImg) {
        foreach ($config['thumbSizes'] as $prefix => [$w, $h]) {
            $thumbPath = $targetDir . '/' . $prefix . '_' . $filename;
            $thumb = imagescale($srcImg, $w, $h, IMG_BICUBIC);
            // 加水印
            if ($config['watermark'] && is_file($config['watermark'])) {
                $wm = imagecreatefrompng($config['watermark']);
                $wmW = imagesx($wm); $wmH = imagesy($wm);
                $dstX = imagesx($thumb) - $wmW - 10;
                $dstY = imagesy($thumb) - $wmH - 10;
                imagecopy($thumb, $wm, $dstX, $dstY, 0, 0, $wmW, $wmH);
            }
            imagejpeg($thumb, $thumbPath, 85);
            $thumbs[$prefix] = $dateDir . '/' . $prefix . '_' . $filename;
            imagedestroy($thumb);
        }
        imagedestroy($srcImg);
    }
}

// 9. 入庫(略)
// $db->insert('attachments', ['path' => $dateDir.'/'.$filename, 'thumbs' => json_encode($thumbs), ...]);

// 10. 返回結果
echo json_encode([
    'code' => 0,
    'data' => [
        'url'   => '/uploads/' . $dateDir . '/' . $filename,
        'thumbs' => $thumbs,
    ]
]);

關鍵安全點

  1. MIME 二次驗證:finfo 讀取文件頭,防止偽造 $_FILES['type']。
  2. 隨機文件名:bin2hex(random_bytes(8)) 避免覆蓋、遍歷、執行腳本。
  3. 上傳目錄不可執行:Nginx/Apache 配置 location /uploads { php_flag engine off; }。
  4. 文件擴展名白名單:雙重保障(MIME + 擴展名)。
  5. 大小限制雙層:PHP upload_max_filesize + 代碼邏輯 maxSize。

進階:分片上傳大文件

超過 100MB 文件建議採用前端分片(如 Uploader.js、WebUploader),後端合併:

// 合併邏輯
$chunkDir = $targetDir . '/chunks_' . $fileHash;
$finalPath = $targetDir . '/' . $filename;
$fp = fopen($finalPath, 'wb');
for ($i = 0; $i < $totalChunks; $i++) {
    fwrite($fp, file_get_contents("{$chunkDir}/{$i}"));
}
fclose($fp);
// 校驗 MD5/SHA1
if (hash_file('sha1', $finalPath) !== $fileHash) unlink($finalPath);
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved