【Next.js】iPhoneで撮影した写真の拡張子、HEIFとHEICをJPGに変換する方法
はじめに
初めまして、現在エンジニアをしながら起業をしてサービスを開発中の橋田至です。
私は今Swappyという同人誌のフリマサイトを開発中です。
同人誌を売買するという観点から、ネイティブアプリでストアで公開するのは不可能だろうという考えから、ブラウザアプリとして開発を進めています。
iPhoneから撮影した写真は特有の拡張子になってしまうバグ
フリマサイトを作るとなると、モバイルファーストにすべきだと感じました。
そこで、iPhoneから撮影した写真をそのままwebにアップロードしてもらえた方がいいと感じます。
ファイルアップロードはCloudinaryというSaaSを使用しているのですが、ファイルアップロード時にiPhone特有の拡張子になってしまうことが原因でファイルアップロード時にエラーが発生していました。
HEIFとHEICとは?
要するにスマホ用の画像の拡張子です。こいつが対応してないライブラリやブラウザがあったりして非常に辛いです😭
今回は画像のupload時にheicかheif立った場合、jpgに変換する処理を作成しました。
変換ライブラリがRSCだと動かない
heic2anyというライブラリを使用したのですが、これはクライアントサイドじゃないと動きませんでした。
そこで、"use client"を使用している部分に使うようにコードを修正し、なんとか動くようになりました。
const processedFiles = await Promise.all(
droppedFiles.map(async (file) => {
const ext = file?.name.split(".").pop()?.toLowerCase();
if (ext === "heic" || ext === "heif") {
try {
if (typeof window !== "undefined") {
// import heic2any from "heic2any";
// eslint-disable-next-line @typescript-eslint/no-var-requires
const heic2any = require("heic2any");
const output = await heic2any({
blob: file,
toType: "image/jpeg",
quality: 0.7,
});
// Check if the output is a single Blob or an array of Blobs
const outputBlob = Array.isArray(output) ? output[0] : output; // Assuming we use the first Blob if it's an array
// Create a new File object from the Blob
const newName = file.name.replace(/\.(heic|heif)$/i, "") + ".jpg";
return new File([outputBlob], newName, {
type: "image/jpeg",
});
}
} catch (error) {
console.error("Error converting HEIC/HEIF file:", error);
return file; // Return the original file in case of an error
}
} else {
return addGrayBackground(file);
}
}),
);
window is not defined
普通にライブラリをimportすると、上記のエラーが発生しました。
これに対処するために、requireを使い、if文の中で使用するとこでエラーを回避しています。
とはいえこのコードでいいのか??
このライブラリも全然バージョンがないので、更新はあまりされていないように見えます。
さらに冗長なコードもあると思われるので、より適切にNext.js(App Router)でHEIC,HEIF対応しているという方がいましたら教えていただけると幸いです。
issueなどを探すと他の方も結構困っているように見えました。
最後に
その他にも「こうすれば良いよ!」などの設定などありましたらお声かけいただけると嬉しいです!
また、Swappyの開発者は随時募集しておりますので、気軽にDMください!!
参考
Discussion
はじめまして。
libheif-jsのページを見ていたらこんなライブラリが紹介されていました。
私自身は使ったことがないので何とも言えませんが、NodeでもBrowserでも動くようです。
要件に合っているのかわかりませんが、ご参考まで。
ありがとうございます!
こちらも使用してみたのですが、内部でfsが使われているようでこちらを使用した場合もエラーが発生したんですよね、、、
ただうまくやればこちらも使用できるのかなと思いますが僕は正しく動かすことができませんでした、、、
環境やエラー内容が不明なので何とも言えませんが、考えられる可能性としては
require('heic-convert')
している(クライアント側で使用したい場合に試すべきはrequire('heic-convert/browser')
の方)require('heic-convert')
やrequire('fs')
するとエラーが出る場合はheic-convert
ではなく環境設定の問題かな、と思います。
このライブラリは一応「Nodeでもブラウザでも動くと思う」とREADMEにあったので、heic-convertと依存先のheic-decodeのソースをざっと眺めてみましたが、たしかに
heic-convert/browser
の方ではfs
を始めとしてNode依存のモジュールは使われていないようでした(その先の依存関係で使われている可能性はありますが)。2のケースの場合はwebpackの設定でクライアントのバンドルからNode依存のライブラリを除外していない可能性が考えられるかと思います。
これ以上は私も知見がなくお力になれそうにありませんが、無事解決できることを祈っております。