production.log

株式会社リブセンスでエンジニアをやっている星直史のブログです。

ExpoのMediaLibraryからiOSのHEICファイルをアップロードした場合にリサイズできない問題の解決方法

概要

会社で作っているSnapmartアプリは写真のアップロードがメインのアプリです。
Snapmartにおける写真アップロード処理は、ざっくり以下の手順で処理されます。

  1. スマホからS3にアップロード
  2. S3にアップロードされた画像ベースにLambdaでサムネイル作成

上記で問題としては上がったのは、HEICファイルがアップロードされると、サムネイル作成処理が失敗することでした。*1

今回は、iOSのHEICファイルをアップロードした場合にリサイズできない問題の解決方法について紹介します。

原因

この不具合のトリガーとなったのは、Expo SDK V35からV37へのアップグレード時、写真選択処理のAPIをCameraRollからMediaLibraryに変更したことでした。
CameraRollは、getPhotos()のreturnでedges.node.image.uriで画像のパスが取得できます。この時、画像がHEICの場合はCameraRollがJPEG形式に変換したあとの画像のパスを返していました。
一方、MediaLibraryはgetAssetsAsync()のreturnで取得するassets.uriおよび、getAssetInfoAsync()のreturnで取得するlocalUriは、HEICファイルのパスを示すため、「1. スマホからS3にアップロード」時にHEICでアップロードされるか、JPEGでアップロードされるかの違いがありました。

また、「2. S3にアップロードされた画像ベースにLambdaでサムネイル作成」ではこれまでJPEGファイルが元画像となる前提の処理であったため、HEICが渡されるとエラーにより、サムネイルが作成されませんでした。

対策

対策は2つあります。

  1. サムネイル作成処理において、元画像がHEICでもサムネイル作成できるようにする
  2. 手元の端末でHEICからJPEGに変換して、変換後のJPEGファイルをアップロードする。

今回はReact Native + Expoアプリで対応することに焦点を当てるので、「2. 手元の端末でHEICからJPEGに変換して、変換後のJPEGファイルをアップロードする。」ことについて説明します。

ImageManipulatorでHEICからJPEGに変換する

ImageManipulatorとは、Expoが提供するローカルファイルシステムに保存されている画像を変更するためのAPIです。 このImageManipulatorのmanipulateAsync(uri, actions, saveOptions)メソッドを使用すると、画像をAPIを通して編集することができます。
また、manipulateAsync()メソッドでは、保存時に引数で指定されたsaveOptionsでフォーマットを指定できます。指定できるフォーマットはJPEGまたはPNGです。(JPEGがデフォルト)

import * as ImageManipulator from "expo-image-manipulator";
...
const assetInfo = await MediaLibrary.getAssetInfoAsync(asset)
if (Platform.OS === "ios" && (assetInfo.localUri.endsWith(".heic") || assetInfo.localUri.endsWith(".HEIC"))) {
  const manipResult = await ImageManipulator.manipulateAsync(assetInfo.localUri)
  console.log(manipResult.uri)
}

今回は、何の加工もせず、ただ単純にHEICからJPEGに変換するだけであるため、manipulateAsyncメソッドの第二引数と第三引数は指定しません。
ドキュメントはこちらです。

docs.expo.io

まとめ

HEICファイルをアップロードした時に、リサイズができなかった原因は、写真選択処理のAPIをCameraRollからMediaLibraryに変更したことでした。
これは、CameraRollはJPEGに変換したファイルを提供することに対して、MediaLibraryはローカルに保存されているファイルのURIを返すことにありました。
アプリ内でうまいことやるには、ImageManipulatorを使用します。
ImageManipulatorは画像を加工するAPIなのですが、保存時のフォーマットはJPEGもしくはPNGです。
そのため、MediaLibraryで取得したHEICを元データとして、ImageManipulator.manipulateAsync()を呼び出すことで、HEICをJPEGに変換させることができました。

*1:HEIF(ヒーフ)とはiOS 11から登場した「High Efficiency Image File Format」の略で「高効率画像ファイル」に使われる拡張子です。高画質のままで画像の容量が軽いのが特徴で、JPEGの半分くらいの容量に抑えられます。