跪拜 Guibai
← All articles
Frontend

When iPhone Users Upload HEIC, Your Frontend Crashes: The Fix That Works

By 小码哥_常 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Any web app that accepts photo uploads from iPhone users will eventually receive an HEIC file, and without conversion it can white-screen the page and send users away. Handling it client-side cuts upload size, but because browser support is patchy and serverless builds fight C++ dependencies, the only reliable pattern is a client conversion with a server-side fallback.

Summary

A mini-program that let users upload ID photos for resumes worked in testing but white-screened on launch day. The culprit was HEIC, the default image format on iOS since version 11. HEIC uses HEVC/H.265 encoding and produces files roughly half the size of JPEG at equal quality, which is why Apple adopted it. But outside the Apple ecosystem support is thin: Chrome only partially supports it from late 2020 and then only when the operating system can decode HEVC, Firefox does not support it at all, and most backend image libraries ignore it. The upload flow used FileReader, an Image object, and Canvas, and the failure happened when the browser tried to load the HEIC into an Image object and fired onerror.

The author evaluated three approaches. Server-side conversion with Node's heic-convert works but pulls in libheif with C++ dependencies that compile fine in Docker yet become painful inside serverless functions like AWS Lambda. It also forces users to upload the original multi-megabyte HEIC first, lengthening the whole round-trip. Client-side conversion using the heic2any library runs WebAssembly in the browser, turning HEIC into a JPEG Blob before any upload. That saves bandwidth, but the decoder adds about 600KB (200KB gzipped), so the author lazy-loads it only when a file's type or extension indicates HEIC. The final production solution combines both: attempt client conversion, catch failures, and upload the original file for the server to convert as a fallback.

The author also notes that file type detection should not rely only on MIME type, since file.type can be empty; checking the extension and magic number is safer. HEIC has two MIME spellings, image/heic and image/heif, and both need handling. Client-side conversion can take 3-5 seconds for a 4000x3000 photo on mobile, so a loading indicator is necessary. For quick one-off conversions the author points to an in-browser online tool that never uploads files, useful for non-technical colleagues who capture product photos on iPhones.

Takeaways
— HEIC is Apple's default image format since iOS 11, using HEVC/H.265 encoding, and produces files about half the size of JPEG at similar quality.
— Chrome only partially supports HEIC starting in late 2020, and only when the operating system provides HEVC decoding; Firefox does not support it.
— In the author's flow, HEIC broke at the step where the browser assigned the DataURL to an Image object, triggering onerror and a white screen.
— Server-side conversion with Node's heic-convert requires libheif C++ dependencies that compile fine in Docker but are cumbersome in serverless functions like AWS Lambda.
— The client-side heic2any library uses WebAssembly; the decoder is about 600KB raw and 200KB gzipped, best lazy-loaded only when a HEIC file is detected.
— The final production fix runs client-side WASM conversion first and falls back to server-side heic-convert if the client conversion throws.
— File type detection should check MIME type, file extension, and magic number, because file.type can be an empty string in some browsers.
— HEIC has two MIME type spellings, image/heic and image/heif, and both must be checked.
— Client-side HEIC decoding can take 3-5 seconds for a 4000x3000 photo on a phone, so a loading state is required to avoid the impression of a hung page.
Conclusions

HEIC support is not just a browser feature; it depends on operating-system-level HEVC decoding, so even a modern browser can refuse the format on an older OS, making client-side conversion a fragile bet.

The dual strategy treats a failed client conversion as an expected event, which is the right assumption for a consumer app where browser versions, OS updates, and WebAssembly availability vary widely.

Uploading an original HEIC first wastes both user bandwidth and server processing time, which is why a client-side conversion wins even at a 200KB decoder cost for normal users.

The author's checklist about checking magic numbers and both MIME spellings reflects how browser inconsistencies are the rule, not the exception, in file-upload code.

Concepts & terms
HEIC
High Efficiency Image Container, Apple's default image format since iOS 11. It wraps HEVC-encoded image data and typically halves file size compared with JPEG at similar quality.
HEVC (H.265)
High Efficiency Video Coding, the video compression standard that HEIC uses for still images. It offers better compression than the older H.264 but requires more computing power to decode, and browser support depends on the operating system's codec availability.
Magic number
A fixed sequence of bytes at the start of a file that identifies its format, regardless of file extension or MIME type. Checking it defends against empty or misleading type metadata in browsers.
WASM (WebAssembly)
A binary instruction format that runs in modern browsers at near-native speed. Libraries like heic2any compile codecs to WASM so the browser can decode formats the native engine does not support.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗