iOS 裝置拍照時,相機感測器固定方向,實際畫面旋轉資訊寫入 JPEG EXIF 的 Orientation 標籤(1~8)。若直接顯示不處理,圖片會出現 90°、180°、270° 旋轉或鏡像。
| 值 | 說明 | 需旋轉角度 |
|---|---|---|
| 1 | 正常 | 0° |
| 2 | 水平鏡像 | 0° + flip H |
| 3 | 旋轉 180° | 180° |
| 4 | 垂直鏡像 | 0° + flip V |
| 5 | 垂直鏡像 + 逆時針 90° | -90° + flip H |
| 6 | 順時針 90° | 90° |
| 7 | 水平鏡像 + 順時針 90° | 90° + flip H |
| 8 | 逆時針 90° | -90° (或 270°) |
/**
* 依據 EXIF Orientation 自動旋轉/鏡像圖片資源
* @param string $tmpPath $_FILES['file']['tmp_name']
* @return resource|false GD 圖片資源,失敗回傳 false
*/
function autoOrientImage(string $tmpPath) {
// 1. 讀取二進位內容建立 GD 資源(支援 JPEG/PNG/GIF/WebP)
$image = @imagecreatefromstring(file_get_contents($tmpPath));
if (!$image) return false;
// 2. 讀取 EXIF
$exif = @exif_read_data($tmpPath);
if (empty($exif['Orientation'])) return $image; // 無標籤或已正常
// 3. 依 Orientation 處理
switch ((int)$exif['Orientation']) {
case 2: // 水平鏡像
imageflip($image, IMG_FLIP_HORIZONTAL);
break;
case 3: // 180°
$image = imagerotate($image, 180, 0);
break;
case 4: // 垂直鏡像
imageflip($image, IMG_FLIP_VERTICAL);
break;
case 5: // 垂直鏡像 + -90°
imageflip($image, IMG_FLIP_VERTICAL);
$image = imagerotate($image, -90, 0);
break;
case 6: // 90°
$image = imagerotate($image, 90, 0);
break;
case 7: // 水平鏡像 + 90°
imageflip($image, IMG_FLIP_HORIZONTAL);
$image = imagerotate($image, 90, 0);
break;
case 8: // -90°
$image = imagerotate($image, -90, 0);
break;
default: // 1 或其他
break;
}
return $image;
}
// 使用範例
$oriented = autoOrientImage($_FILES['photo']['tmp_name']);
if ($oriented) {
imagejpeg($oriented, 'uploads/fixed.jpg', 90);
imagedestroy($oriented);
}
imagecreatefromjpeg 更通用,自動偵測格式0 為背景色(黑),若需透明背景需改用 imagecreatetruecolor + imagesavealphaimagecopyresampled 手動鏡像imagedestroy() 釋放記憶體實測多數 Android 手機 Orientation 固定為 1(已由相機 App 硬體旋轉),無需處理。但部分廠商(如三星舊機型)仍會寫入 Orientation,建議統一跑自動修正流程。
現代瀏覽器支援 CSS image-orientation: from-image,可直接交給瀏覽器渲染:
img { image-orientation: from-image; }
但為相容舊瀏覽器、非 Web 場景(PDF 生成、縮圖處理),伺服器端修正仍是最保險做法。
上傳圖片處理流程建議:
autoOrientImage() 修正方向一鍵解決 iOS 拍照「躺著看」的經典問題。