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

PHP + AJAX 無刷新圖片上傳:jQuery Form Plugin + 後端驗證 + 預覽回顯完整流程

     編輯:PHP 相關知識

PHP + AJAX 無刷新圖片上傳完整流程

圖片上傳是 CMS、電商、社區的高頻功能。本文採用 jQuery Form Plugin 實現 iframe 級別的無刷新上傳(兼容 IE9+),結合後端 類型/大小/尺寸驗證、隨機重命名、縮略圖生成、JSON 回傳預覽 URL,構建生產可用的上傳組件。

前端依賴

<!-- 頁面頭部引入 -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jquery-form@4.3.0/dist/jquery.form.min.js"></script>
<!-- 可選:圖片壓縮預覽 (客戶端縮放) -->
<script src="https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.1/dist/browser-image-compression.min.js"></script>

HTML 結構

<div class="upload-box">
    <input type="hidden" name="avatar" id="avatarUrl" value="">
    <div class="preview" id="preview"><!-- 圖片預覽區 --></div>
    <div class="upload-btn-wrap">
        <span class="btn-text">上傳頭像</span>
        <input type="file" name="avatar_file" id="avatarFile" accept="image/*" hidden>
    </div>
    <p class="tip" id="uploadTip"></p>
</div>

JS 交互邏輯

<script>
$(function() {
    const $file = $('#avatarFile');
    const $preview = $('#preview');
    const $url = $('#avatarUrl');
    const $tip = $('#uploadTip');
    const $btnText = $('.btn-text');
    
    // 點擊按鈕觸發文件選擇
    $('.upload-btn-wrap').on('click', () => $file.trigger('click'));
    
    // 文件選擇後自動提交
    $file.on('change', function() {
        if (!this.files.length) return;
        const file = this.files[0];
        
        // 客戶端預驗證
        if (!/^image\//.test(file.type)) { $tip.text('請選擇圖片文件').css('color','red'); return; }
        if (file.size > 5 * 1024 * 1024) { $tip.text('文件不能超過 5MB').css('color','red'); return; }
        
        // 可選:客戶端壓縮 (大文件上傳前縮放)
        // imageCompression(file, { maxSizeMB: 1, maxWidthOrHeight: 800, useWebWorker: true })
        //   .then(compressed => upload(compressed))
        //   .catch(console.error);
        // return;
        
        upload(file);
    });
    
    function upload(file) {
        $tip.text('上傳中...').css('color','#666');
        $btnText.text('上傳中...');
        
        // 使用 jQuery Form Plugin 的 ajaxSubmit
        $file.closest('form').length || $file.wrap('
'); $file.parent().ajaxSubmit({ dataType: 'json', beforeSubmit: () => { $preview.empty(); $url.val(''); }, success: function(res) { if (res.code === 200) { $url.val(res.data.url); $preview.html(`頭像`); $tip.text('上傳成功').css('color','green'); $btnText.text('重新上傳'); } else { $tip.text(res.msg || '上傳失敗').css('color','red'); $btnText.text('上傳頭像'); } }, error: function(xhr) { $tip.text('網絡錯誤: ' + xhr.status).css('color','red'); $btnText.text('上傳頭像'); } }); } }); </script>

後端處理:api/upload_avatar.php

<?php
// api/upload_avatar.php
require_once '../db.php'; // PDO 連接
header('Content-Type: application/json; charset=utf-8');

// 1. 基礎檢查
if ($_SERVER['REQUEST_METHOD'] !== 'POST') exit(json_encode(['code'=>405,'msg'=>'Method Not Allowed']));
if (!isset($_FILES['avatar_file']) || $_FILES['avatar_file']['error'] !== UPLOAD_ERR_OK) {
    exit(json_encode(['code'=>400,'msg'=>'未收到文件或上傳錯誤']));
}

$file = $_FILES['avatar_file'];
$allowedTypes = ['image/jpeg','image/png','image/gif','image/webp'];
$maxSize = 5 * 1024 * 1024; // 5MB
$maxDim = 2000; // 最大寬/高 2000px

// 2. 類型驗證(檢測真實 MIME,防偽裝)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
if (!in_array($mime, $allowedTypes)) {
    exit(json_encode(['code'=>400,'msg'=>'不支持的圖片格式,僅允許 JPG/PNG/GIF/WebP']));
}
if ($file['size'] > $maxSize) {
    exit(json_encode(['code'=>400,'msg'=>'文件大小超過 5MB 限制']));
}

// 3. 尺寸驗證
list($w, $h) = getimagesize($file['tmp_name']);
if ($w > $maxDim || $h > $maxDim) {
    exit(json_encode(['code'=>400,'msg'=>"圖片尺寸超過限制 ({$maxDim}px)"]));
}

// 4. 生成唯一存儲路徑
$ext = match($mime) {
    'image/jpeg' => 'jpg',
    'image/png' => 'png',
    'image/gif' => 'gif',
    'image/webp' => 'webp',
};
$dateDir = date('Y/m/d');
$saveDir = __DIR__ . "/../uploads/avatars/{$dateDir}";
if (!is_dir($saveDir)) mkdir($saveDir, 0755, true);
$fileName = bin2hex(random_bytes(8)) . ".{$ext}";
$savePath = $saveDir . '/' . $fileName;

// 5. 移動文件(可選:生成縮略圖)
if (!move_uploaded_file($file['tmp_name'], $savePath)) {
    exit(json_encode(['code'=>500,'msg'=>'文件保存失敗']));
}

// 可選:生成 120x120 縮略圖
$thumbPath = $saveDir . '/thumb_' . $fileName;
$src = imagecreatefromstring(file_get_contents($savePath));
$thumb = imagescale($src, 120, 120);
switch($ext) {
    case 'jpg': imagejpeg($thumb, $thumbPath, 85); break;
    case 'png': imagepng($thumb, $thumbPath, 8); break;
    case 'webp': imagewebp($thumb, $thumbPath, 85); break;
}
imagedestroy($src); imagedestroy($thumb);

// 6. 返回可訪問 URL
$webPath = "/uploads/avatars/{$dateDir}/{$fileName}";
$thumbWebPath = "/uploads/avatars/{$dateDir}/thumb_{$fileName}";

echo json_encode([
    'code' => 200,
    'msg' => '上傳成功',
    'data' => [
        'url' => $webPath,
        'thumb' => $thumbWebPath,
        'width' => $w,
        'height' => $h,
        'size' => $file['size']
    ]
]);

關鍵安全與體驗細節

環節風險對策
文件類型偽裝 .php 為 .jpgfinfo_file() 檢測真實 MIME + 白名單
文件大小佔滿磁盤/內存upload_max_filesize/post_max_size + 代碼二次校驗
文件名路徑遍歷/覆蓋/注入random_bytes(8) 生成隨機名,僅保留驗證後的副檔名
存儲路徑Web 可直接訪問執行上傳目錄配置 Nginx location ~ \.php$ { deny all; } 或存儲於非 Web 根目錄
圖片處理解壓炸彈/畸形圖片導致 GD 崩潰設置 ini_set('gd.jpeg_ignore_warning', 1)、限制最大像素 ini_set('memory_limit','256M')
併發上傳同名覆蓋隨機文件名 + 日期目錄分散

進階:拖拽上傳 + 多文件隊列

// HTML5 Drag & Drop
const dropZone = document.querySelector('.upload-box');
['dragenter','dragover'].forEach(e => dropZone.addEventListener(e, e => { e.preventDefault(); dropZone.classList.add('dragover'); }));
['dragleave','drop'].forEach(e => dropZone.addEventListener(e, e => { e.preventDefault(); dropZone.classList.remove('dragover'); }));
dropZone.addEventListener('drop', e => {
    e.preventDefault();
    const files = e.dataTransfer.files;
    if (files.length) handleFiles(files); // 复用上传逻辑
});

總結

此方案以 jQuery Form Plugin 解決無刷新上傳兼容性,後端以 finfo + 尺寸檢查 + 隨機命名 + 非 Web 根目錄存儲 構建安全防線,前端提供 即時預覽、壓縮選項、拖拽支持,可直接作為項目通用上傳組件使用。

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