Step 3: Core Technology
17. iPhoneのHEIC画像が
ブラウザで表示できない問題
HEICを表示できるかはブラウザ、OS、画像の構成で変わります。このサイトの画像変換には、必要なときにheic2anyを取得する分岐があります。採用版と実際の変換順を確認し、対応範囲やメタデータを残せるかを、公式資料と区別して読んでいきます。
1. HEICとは何か?なぜブラウザで表示できないのか
HEIFは画像を格納するコンテナー形式で、HEVCで符号化した画像に .heic の拡張子がよく使われます。iPhoneの高効率設定で保存された写真などが該当します。画質とサイズの関係は画像や設定によって異なります。
表示できるかはブラウザ・OS・画像の符号化方式に依存します。Safari 17では対応OS上でHEIC表示が追加されており、「Safariでも必ず表示できない」という説明は正確ではありません。アプリでは対応状況を確認し、非対応画像の変換やエラー表示を用意します。
2. ブラウザ内の解決策:heic2any の導入
この画像変換の入力処理は、選択したFileをブラウザ内のデコーダーとCanvasへ渡します。この処理のために画像をサーバーへ送信するコードは含めていません。
このサイトでは、ブラウザ上で動作するJavaScriptライブラリ heic2any を使います。これはlibheifを利用したデコード処理をブラウザ側で行い、JPEGやPNGなどへ変換するライブラリです。利用するバージョンの実装と対応範囲を確認し、WebAssemblyを使うかどうかだけで性能を判断しないようにします。
実装:HEICを検知して自動変換するロジック
ユーザーがファイルを選択した際、MIMEタイプと拡張子を手がかりに、変換を試す対象を判定できます。これらは形式を保証する検査ではなく、実際のデコード失敗も扱う必要があります。
【具体的なコード】heic2any による非同期変換
// ファイルが選択された時のハンドラ
async function handleImageSelect(file) {
let targetFile = file;
// MIMEタイプが image/heic なら変換実行
if (/^image\/(heic|heif)$/i.test(file.type) || /\.hei[cf]$/i.test(file.name)) {
try {
// ローディングインジケーターを表示
showSpinner("iPhone形式の画像を変換中...");
const result = await heic2any({
blob: file,
toType: "image/jpeg",
quality: 0.8
});
// 複数画像の場合は先頭の1枚を扱う方針の例
const convertedBlob = Array.isArray(result) ? result[0] : result;
if (!(convertedBlob instanceof Blob)) throw new Error("画像がありません");
const baseName = file.name.replace(/\.[^.]+$/, "");
targetFile = new File([convertedBlob], `${baseName}.jpg`, {
type: "image/jpeg"
});
} catch (error) {
showErrorMessage("画像の変換に失敗しました。");
return;
} finally {
hideSpinner();
}
}
// 変換した場合はJPEG、変換対象外なら元の形式として読み込む
processImage(targetFile);
} 3. 注意点と落とし穴:リソース管理とUX
変換を組み込むときは、処理の場所、取得コスト、保持するデータを確認します。
- CPU負荷と変換時間: HEICのデコードは非常に重い処理です。特にスペックの低いスマートフォンでは、画像サイズや端末によって変換時間が大きく変わります。ユーザーが「フリーズした」と誤解しないよう、明確なローディング表示(スピナーなど)が必須です。
- ライブラリのサイズ: 画像デコーダーを含むため、ライブラリの取得・解析コストがあります。必要な時だけ動的にインポート(Dynamic Import)するなどの工夫をしないと、ページ全体の読み込みが遅くなる可能性があります。
- メモリ消費: 大きな画像(4K以上など)を変換すると、ブラウザのメモリを一時的に大量消費します。不要なBlobへの参照やBlob URLを保持し続けない設計が必要です。Blob自体に明示的なfreeメソッドがあるわけではありません。
4. 変換に失敗した時も、次の操作を案内する
変換の処理が失敗しても、処理中表示を終え、画像を選び直せる状態へ戻します。利用者には失敗した段階を短く伝えます。形式の非対応と、ライブラリを取得できない通信エラーは、次に試す操作が違います。
対応画像をそのまま読み込む入口を用意する一方、変換失敗や形式の差がある場合は元画像を残し、別形式で試せる案内を用意します。
変換後に確認すること
この例は heic2any を読み込んだ環境を前提とし、showSpinner・showErrorMessage・hideSpinner・processImage はアプリ側のUI関数です。複数画像を含む入力は先頭1枚だけ使う例なので、全画像の保存が必要なら配列を個別に扱います。
- 出力の向き、色、画素数が意図どおりかを元画像と見比べる。
- 撮影日時や位置情報などのメタデータが保持されると仮定しない。
- 破損ファイルや非対応画像では、JPEG/PNGへ変換して再選択する方法を案内する。
拡張子を .jpg に変えるだけでは変換になりません。原本を残し、加工用のコピーを作って確認します。
採用版0.0.4と、本番の変換分岐を確認する
画像変換ツールで確認した採用版はheic2any 0.0.4です。本番は拡張子がheic/heifの場合に動的importし、JPEG品質0.9で変換します。複数結果なら先頭の1枚を後続のCanvasへ渡す設計です。
const heicModule = await import('heic2any');
const heic2any = heicModule.default || heicModule;
const converted = await heic2any({ blob: file, toType: 'image/jpeg', quality: 0.9 });
processBlob = Array.isArray(converted) ? converted[0] : converted;本番の抜粋で、npmで導入済みのビルド環境を前提にします。独立したVite等の開発プロジェクトならnpm.cmd install [email protected]で版を固定します。単独HTMLへimport文だけを貼って動く構成ではありません。制作解説では変換後のCanvas処理へ進みます。
まとめ
HEIC問題は、単なるフォーマットの違いではなく、OSの壁とブラウザの限界がぶつかり合うフロントエンドの難所です。ブラウザで使える画像デコーダーを利用すれば、対応するHEIC画像を端末内で変換できます。ただし、すべてのHEIFの構成やメタデータをそのまま維持できるとは限りません。
動作する入力を一つ確認した後、別の画像構成・ブラウザ・保存方法を追加で試します。どこまで確認したかを記録すると、対応形式の説明を実際の結果に合わせられます。
参考資料
heic2any公式プロジェクト(英語)(別タブで開く)は、メタデータを維持する用途が主目的ではないこと、ブラウザ環境が必要なことを説明しています。同READMEのブラウザ対応に関する古い記述は、WebKit公式のSafari 17.0更新(英語)(別タブで開く)のHEIC対応追加と照合します。この記事では採用コードを確認していますが、すべてのHEIC入力やiPhone実機での変換成功を測定したものではありません。