判斷 GIF 是否為動畫(多幀),核心邏輯是檢測檔案二進制中是否存在 **Netscape 2.0 應用擴展塊** (`0x21 0xFF 0x0B NETSCAPE2.0`),該塊僅出現在動畫 GIF 中。本文提供純 PHP 二進制檢測與 GD 庫兩種實作,並分析準確度邊界。
<?php
/**
* 檢測 GIF 是否為動畫
* @param string $filePath GIF 檔案路徑
* @return bool true=動畫, false=靜態或非 GIF
*/
function isAnimatedGif(string $filePath): bool {
// 1. 基礎驗證
if (!is_file($filePath) || !is_readable($filePath)) return false;
$size = filesize($filePath);
if ($size < 13) return false; // 最小 GIF 標頭
// 2. 讀取前 1KB 足夠判斷 (標頭+首幀通常在前)
$fp = fopen($filePath, 'rb');
$header = fread($fp, min(1024, $size));
fclose($fp);
// 3. 驗證 GIF 標頭
if (strncmp($header, 'GIF', 3) !== 0) return false;
// 4. 搜尋 Netscape 2.0 應用擴展塊特徵碼
// 序列: 0x21 0xFF 0x0B 'NETSCAPE2.0'
$needle = chr(0x21) . chr(0xFF) . chr(0x0B) . 'NETSCAPE2.0';
return strpos($header, $needle) !== false;
}
// 使用
var_dump(isAnimatedGif('static.gif')); // false
var_dump(isAnimatedGif('animated.gif')); // true
?>
<?php
/**
* 使用 GD 獲取 GIF 幀數
* @return int 幀數 (0=非GIF/讀取失敗, 1=靜態, >1=動畫)
*/
function getGifFrameCountGd(string $filePath): int {
$im = @imagecreatefromgif($filePath);
if (!$im) return 0;
// GD 並無直接取得幀數 API,需嘗試多次 imagecreatefromgif
// 實際上 PHP GD 僅支援讀取第一幀,無法原生獲取總幀數
// 此處為概念代碼,實際需用 imagick 或 解析二進制
imagedestroy($im);
return 1; // GD 限制
}
// 真正支援幀數的方案:Imagick
function getGifFrameCountImagick(string $filePath): int {
if (!class_exists('Imagick')) return -1;
try {
$imagick = new Imagick($filePath);
$count = $imagick->getNumberImages();
$imagick->destroy();
return $count;
} catch (ImagickException $e) {
return -1;
}
}
?>
| 維度 | 二進制特徵碼 | GD Library | Imagick |
|---|---|---|---|
| 依賴 | 無 | PHP GD (內建) | ImageMagick + php-imagick |
| 速度 | 極快 (讀 1KB) | 快 (解碼首幀) | 中等 (完整解析) |
| 準確度 | 高 (標準動畫必有 Netscape 塊) | 低 (無法獲取總幀數) | 最高 (真實幀數) |
| 記憶體 | 極低 | 中等 | 高 (解碼所有幀) |
| 適用場景 | 海量檔案快速篩選 | 需同時處理圖片資訊 | 需精確幀數、延遲、尺寸 |
<?php
function isAnimatedGifRobust(string $filePath): bool {
$size = filesize($filePath);
if ($size < 13) return false;
$fp = fopen($filePath, 'rb');
// 讀取前 64KB 或完整檔案 (取較小)
$chunk = fread($fp, min(65536, $size));
fclose($fp);
if (strncmp($chunk, 'GIF', 3) !== 0) return false;
$needle = chr(0x21) . chr(0xFF) . chr(0x0B) . 'NETSCAPE2.0';
return strpos($chunk, $needle) !== false;
}
?>
二進制特徵碼檢測以「零依賴、極速、準確度高」成為生產環境判斷 GIF 動畫的首選方案;需精確幀數時引入 Imagick。