用十六進位編輯器開啟一個 HEIC 檔案。前十二個位元組:
00 00 00 18 66 74 79 70 68 65 69 63
這是 ISOBMFF —— MP4 使用的同一個容器標準。0x18(24 位元組)是 box 大小,ftyp 是檔案類型 box,heic 是主品牌。HEIC 檔案不是影像編碼,它是一個帶品牌標記的容器,內部封裝 HEVC 編碼的影像。
HEIC 到底是什麼
HEIC = High Efficiency Image Container。Apple 對 HEIF 的品牌化實作,MPEG 於 2015 年標準化為 ISO/IEC 23008-12。JPEG 同時是壓縮演算法和檔案格式,HEIC 只是一個容器。內部壓縮用的是 HEVC(H.265),與 4K 影片同一個編解碼器。
單個 HEIC 檔案可以儲存:
- 一幅主影像
- 多幅備選影像(連拍)
- 影像序列(原況照片:靜態圖 + 3 秒影片)
- Alpha 通道和深度圖
- 每通道 16 位元色深(JPEG 上限為 8 位元)
容器使用 box 結構 —— 與 MP4 完全一致:
| Box | 作用 |
|---|---|
ftyp | 檔案類型和相容品牌 |
meta | 項目詮釋資料和屬性 |
mdat | 原始編碼影像資料(HEVC 位元流) |
iloc | 項目在 mdat 中的位置 |
正是這種可擴展性讓 HEIC 做到了 JPEG 永遠做不到的事。解析它需要理解 box 層級,而不是簡單讀取原始位元流。
Apple 為什麼切換
Apple 沒有發明 HEIC。MPEG 於 2015 年發布了 HEIF,Apple 在 2017 年的 iOS 11 中將其採納為 iPhone 預設相機格式。
切換的驅動力是算術,不是行銷。一張 1200 萬畫素的 iPhone 照片,JPEG 約 3.5 MB,HEIC 約 1.8 MB。在 50 GB 的免費 iCloud 儲存檔位下,這個差距意味著大約多出 14,000 張照片。Apple 賣儲存檔位,這筆賬不用算第二遍。
另一個因素是生態對齊。Apple 早已在影片中採用 HEVC(iOS 11 中的 H.265)。對靜態影像復用同一個編解碼器,意味著 A 系列晶片上可以共享硬體解碼模組、降低功耗,並且只需要一條授權路徑。
利弊權衡
HEIC 在幾乎所有指標上都優於 JPEG,除了相容性。
| 維度 | HEIC | JPEG |
|---|---|---|
| 壓縮效率 | 同等畫質下小約 40–50% | 基準 |
| 色深 | 最高 16 位元 | 8 位元 |
| 透明度 | 支援 | 不支援 |
| 單檔案多圖 | 支援 | 不支援 |
| 無損再編輯 | 支援 | 不支援 |
| Windows 原生支援 | 需安裝 HEIF 擴充功能 | 通用 |
| Web 瀏覽器支援 | 僅 Safari | 通用 |
| Android 支援 | 9 以上原生支援 | 通用 |
| 專利授權 | HEVC 專利池 | JPEG 免權利金 |
HEVC 被多個專利池覆蓋(MPEG LA、Access Advance)。Apple 替 iOS 使用者支付了授權費,第三方廠商沒有這份待遇。這種不確定性是 Apple 生態系統之外採用停滯的主要原因。
透過讀取檔案簽名偵測 HEIC
不要信任 .heic 副檔名。讀取前 32 位元組,解析 ftyp box。
有效 HEIC 檔案頭的精確位元組布局:
Bytes 0–3: Box 大小(大端 uint32)
Bytes 4–7: Box 類型: "ftyp"(0x66 0x74 0x79 0x70)
Bytes 8–11: 主品牌: "heic" 或 "heif" 或 "mif1"
Bytes 12–15: 次版本號(通常為 0x00000000)
Bytes 16+: 相容品牌列表(如 "mif1"、"heic"、"MiHE")
瀏覽器端 TypeScript:
async function isHeic(file: File): Promise<boolean> {
const buffer = await file.slice(0, 32).arrayBuffer()
const bytes = new Uint8Array(buffer)
if (String.fromCharCode(...bytes.slice(4, 8)) !== "ftyp") return false
const brand = String.fromCharCode(...bytes.slice(8, 12))
return ["heic", "heif", "mif1", "msf1"].includes(brand)
}
我們的轉換器在嘗試解碼前會執行這項檢查。品牌不對 = 立即拒絕,不浪費 CPU。
Python
def is_heic(path: str) -> bool:
with open(path, "rb") as f:
header = f.read(32)
if len(header) < 12:
return False
if header[4:8].decode("ascii", errors="ignore") != "ftyp":
return False
return header[8:12].decode("ascii", errors="ignore") in {"heic", "heif", "mif1", "msf1"}
更高階的偵測可以用 filetype:
import filetype
kind = filetype.guess("photo.heic")
if kind and kind.extension in ("heic", "heif"):
print(kind.mime) # image/heic
或使用 pillow-heif:
from pillow_heif import is_heif
if is_heif("photo.heic"):
# 有效容器
pass
Go
func isHeic(path string) bool {
f, err := os.Open(path)
if err != nil {
return false
}
defer f.Close()
buf := make([]byte, 32)
if _, err := f.Read(buf); err != nil {
return false
}
if string(buf[4:8]) != "ftyp" {
return false
}
brand := string(buf[8:12])
return brand == "heic" || brand == "heif" || brand == "mif1" || brand == "msf1"
}
PHP
function isHeic(string $path): bool {
$header = file_get_contents($path, false, null, 0, 32);
if (strlen($header) < 12) return false;
if (substr($header, 4, 4) !== 'ftyp') return false;
return in_array(substr($header, 8, 4), ['heic', 'heif', 'mif1', 'msf1'], true);
}
使用 fileinfo:
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file('photo.heic');
// image/heic 或 image/heif
ImageMagick CLI
ImageMagick 7+ 在編譯時帶上 libheif 後支援 HEIC:
magick identify -verbose photo.heic | grep "Format:"
# Format: HEIC (High Efficiency Image Container)
如果未編譯 libheif,會返回 no decode delegate for this image format。大多數 Linux 發行版上,安裝 libheif-examples 會提供 heif-convert 作為替代方案。
轉換路徑
知道一個檔案是 HEIC,不代表你能開啟它。Windows 需要 HEIF Image Extensions。大多數瀏覽器拒絕在 <img> 標籤中渲染 HEIC。Android 9+ 原生支援,老舊裝置不支援。
解決方式:轉換。我們的 HEIC 轉 JPG 轉換器 在瀏覽器中處理整個流程:
- 拖曳檔案 — 單張照片或整個資料夾,自動批次處理。
- 簽名驗證 — 上述位元組檢查對每個檔案執行。非 HEIC 檔案立即拒絕並給出清晰提示。
- 客戶端解碼 — libheif 的 WebAssembly 版本在本地執行。不上傳。不觸碰伺服器。
- 保品質輸出 — 90% 品質的 JPG 保留原始解析度和色彩精度。
- 批次下載 — 單個檔案或一個 ZIP 壓縮檔。
需要帶透明度的無損輸出:HEIC 轉 PNG。需要針對 Web 優化的體積:HEIC 轉 WebP。
你的檔案永遠不會離開你的裝置。這才是重點。


