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

PHP 圖片加水印完整實現:上傳處理、文字/圖片水印、縮略圖生成

     編輯:PHP 綜合知識

為保護圖片版權、推廣品牌,網站常需在用戶上傳圖片時自動添加水印。本文提供完整的上傳→驗證→加水印(文字/圖片)→生成縮略圖→保存的流程代碼,基於 GD 庫實現。

核心配置參數

<?php
$config = [
    'max_size' => 2 * 1024 * 1024,        // 2MB
    'dest_dir' => 'uploads/',             // 儲存目錄
    'allow_types' => ['image/jpeg','image/png','image/gif','image/webp'],
    'watermark' => true,                  // 是否加水印
    'wm_type' => 1,                       // 1=文字 2=圖片
    'wm_position' => 4,                   // 1左下 2右下 3左上 4右上 5居中
    'wm_text' => '© Mysite.com',          // 文字水印內容
    'wm_font' => __DIR__ . '/fonts/simhei.ttf', // TTF 字體路徑
    'wm_font_size' => 18,
    'wm_color' => [255,255,255,100],      // RGBA 白色半透明
    'wm_image' => 'watermark.png',        // 圖片水印路徑
    'make_thumb' => true,                 // 生成縮略圖
    'thumb_scale' => 0.5,                 // 縮略比例
];
?>

上傳驗證與儲存

<?php
function handleUpload(array $file, array $cfg): array {
    $res = ['success' => false, 'msg' => '', 'path' => ''];

    if ($file['error'] !== UPLOAD_ERR_OK) {
        $res['msg'] = '上傳錯誤碼: ' . $file['error']; return $res;
    }
    if ($file['size'] > $cfg['max_size']) {
        $res['msg'] = '文件過大'; return $res;
    }
    if (!in_array($file['type'], $cfg['allow_types'], true)) {
        $res['msg'] = '類型不允許'; return $res;
    }

    // 二次驗證 MIME
    $finfo = finfo_open(FILEINFO_MIME_TYPE);
    if (!in_array(finfo_file($finfo, $file['tmp_name']), $cfg['allow_types'], true)) {
        $res['msg'] = '文件內容類型不符'; return $res;
    }

    $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
    $name = date('YmdHis') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
    $dest = rtrim($cfg['dest_dir'], '/') . '/' . $name;

    if (!move_uploaded_file($file['tmp_name'], $dest)) {
        $res['msg'] = '移動失敗'; return $res;
    }

    $res['success'] = true;
    $res['path'] = $dest;
    return $res;
}
?>

GD 加水印核心函數

<?php
function addWatermark(string $srcPath, array $cfg): bool {
    $info = getimagesize($srcPath);
    if (!$info) return false;
    $mime = $info['mime'];

    // 建立源圖資源
    $createMap = [
        'image/jpeg' => 'imagecreatefromjpeg',
        'image/png'  => 'imagecreatefrompng',
        'image/gif'  => 'imagecreatefromgif',
        'image/webp' => 'imagecreatefromwebp',
    ];
    if (!isset($createMap[$mime])) return false;
    $src = $createMap[$mime]($srcPath);

    // 建立真彩畫布並複製(保持透明度)
    $w = imagesx($src); $h = imagesy($src);
    $dst = imagecreatetruecolor($w, $h);
    imagealphablending($dst, false);
    imagesavealpha($dst, true);
    $transparent = imagecolorallocatealpha($dst, 0, 0, 0, 127);
    imagefill($dst, 0, 0, $transparent);
    imagecopy($dst, $src, 0, 0, 0, 0, $w, $h);

    if ($cfg['wm_type'] === 1) { // 文字水印
        $color = imagecolorallocatealpha($dst, ...$cfg['wm_color']);
        $box = imagettfbbox($cfg['wm_font_size'], 0, $cfg['wm_font'], $cfg['wm_text']);
        $tw = $box[2] - $box[0]; $th = $box[1] - $box[7];
        [$x, $y] = calcPosition($w, $h, $tw, $th, $cfg['wm_position'], 10);
        imagettftext($dst, $cfg['wm_font_size'], 0, $x, $y, $color, $cfg['wm_font'], $cfg['wm_text']);
    } elseif ($cfg['wm_type'] === 2 && is_file($cfg['wm_image'])) { // 圖片水印
        $wm = imagecreatefrompng($cfg['wm_image']); // 假設 PNG 透明
        $wmW = imagesx($wm); $wmH = imagesy($wm);
        [$x, $y] = calcPosition($w, $h, $wmW, $wmH, $cfg['wm_position'], 10);
        imagecopy($dst, $wm, $x, $y, 0, 0, $wmW, $wmH);
        imagedestroy($wm);
    }

    // 覆蓋保存
    $saveMap = [
        'image/jpeg' => fn() => imagejpeg($dst, $srcPath, 90),
        'image/png'  => fn() => imagepng($dst, $srcPath, 6),
        'image/gif'  => fn() => imagegif($dst, $srcPath),
        'image/webp' => fn() => imagewebp($dst, $srcPath, 85),
    ];
    $ok = $saveMap[$mime]();
    imagedestroy($src);
    imagedestroy($dst);
    return $ok;
}

function calcPosition(int $W, int $H, int $w, int $h, int $pos, int $margin): array {
    return match($pos) {
        1 => [$margin, $H - $h - $margin],           // 左下
        2 => [$W - $w - $margin, $H - $h - $margin], // 右下
        3 => [$margin, $margin],                     // 左上
        4 => [$W - $w - $margin, $margin],           // 右上
        5 => [($W - $w) / 2, ($H - $h) / 2],         // 居中
        default => [$W - $w - $margin, $H - $h - $margin],
    };
}
?>

縮略圖生成

<?php
function makeThumb(string $srcPath, float $scale = 0.5): ?string {
    $info = getimagesize($srcPath);
    if (!$info) return null;
    $mime = $info['mime'];
    $createMap = [
        'image/jpeg' => 'imagecreatefromjpeg',
        'image/png'  => 'imagecreatefrompng',
        'image/gif'  => 'imagecreatefromgif',
        'image/webp' => 'imagecreatefromwebp',
    ];
    if (!isset($createMap[$mime])) return null;
    $src = $createMap[$mime]($srcPath);
    $w = imagesx($src); $h = imagesy($src);
    $nw = (int)($w * $scale); $nh = (int)($h * $scale);
    $dst = imagecreatetruecolor($nw, $nh);
    imagealphablending($dst, false);
    imagesavealpha($dst, true);
    imagecopyresampled($dst, $src, 0, 0, 0, 0, $nw, $nh, $w, $h);
    $thumbPath = preg_replace('/\.\w+$/', '_thumb$0', $srcPath);
    $saveMap = [
        'image/jpeg' => fn() => imagejpeg($dst, $thumbPath, 85),
        'image/png'  => fn() => imagepng($dst, $thumbPath, 6),
        'image/gif'  => fn() => imagegif($dst, $thumbPath),
        'image/webp' => fn() => imagewebp($dst, $thumbPath, 80),
    ];
    $ok = $saveMap[$mime]();
    imagedestroy($src); imagedestroy($dst);
    return $ok ? $thumbPath : null;
}
?>

完整流程調用

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
    $up = handleUpload($_FILES['image'], $config);
    if (!$up['success']) die($up['msg']);

    if ($config['watermark']) addWatermark($up['path'], $config);
    if ($config['make_thumb']) makeThumb($up['path'], $config['thumb_scale']);

    echo "上傳成功: {$up['path']}";
}
?>

進階建議

  • 大圖處理記憶體不足:ini_set('memory_limit', '256M') 或分塊處理。
  • 高併發:異步隊列 (Redis + Worker) 處理水印/縮略,HTTP 即時返回。
  • 字體版權:使用開源字體 (如思源黑體) 避免侵權。
  • 圖片水印建議預先處理為帶 Alpha 通道的 PNG,imagecopy 直接疊加最自然。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved