背景
iOS11から、写真がHEIC形式で保存されるようになりました。
そこで、HEIC形式の写真を弊社のサービスサイトにアップロードできるように、「.heic」に対応できるAPIをつくることになりました。
ついでに、iPhoneで撮影した写真をアップロードするとなぜか画像が横になってしまう現象もあったので、同時に対応します。
参考:https://qiita.com/RichardImaokaJP/items/385beb77eb39243e50a6
環境(ざっくり)
[ローカル]
mac, node
[本番]
AWS Linux
外部ライブラリ
・heicをjpegに変換するコマンド
[tifig] https://github.com/monostream/tifig
・画像の向きを正面にするために利用したライブラリ
[sharp] https://github.com/lovell/sharp
・shellコマンドを実行するライブラリ
[child_process] https://github.com/npm/security-holder
tifigコマンドにPATHを通す
まずは、ローカル環境と本番環境それぞれでtifigコマンドをたたけるようにしなければなりません。
[Mac(ローカル環境)]
下記のURLの【macOS aka OSX】を参考に、コマンドを叩けるようになります。
https://github.com/monostream/tifig
[AWS Linux(本番環境)]
下記のURLからバイナリファイルを取得。
https://github.com/monostream/tifig/releases
(現在最新:https://github.com/monostream/tifig/releases/download/0.2.2/tifig-static-0.2.2.tar.gz)
PATHが通ったかどうかは--versionで確認できます。
tifig --version
tifig 0.2.2
heicの変換
では実際に変換していきます。
処理の大きな流れとしては
1. アップロードされたheicを一時フォルダに保存する
2. 保存されたheicをtifigコマンドでjpegに変換する
3. iPhoneの写真なので、横向きを縦向きになおす
です。
1の「一時フォルダに保存」された状態からの処理を書いていくので、
以下、2と3の処理を書いていきます。
heicをjpegに変換
const { execSync } = require('child_process');
const { unlinkSync } = require('fs');
const REGEX = /\.[^\.]+$/;
const TYPES = [
'image/heif',
'image/heic',
'image/heif-sequence',
'image/heic-sequence',
];
class HeicConverter {
convert(path, name) {
const oldFilePath = `${path}${name}`;
const newName = name.replace(REGEX, '.jpg');
const newFilePath = `${path}${newName}`;
// ここにセキュリティを入れて!!!!!!!!!!!
const command = `tifig ${oldFilePath} ${newFilePath}`;
try {
execSync(command);
unlinkSync(path);
return Promise.resolve();
} catch (err) {
return Promise.reject(new Error(err));
}
}
isHeicFile(type) {
return TYPES.includes(type);
}
}
module.exports = HeicConverter;
こんな感じのHeicConverterクラスを作成しました。
isHeicFileでHEIC形式のイメージかどうかを判定するメソッドと、
JPEG形式に変換するconvertメソッドを用意しています。
convertメソッドでやっていることはシンプルで、
heicファイルの場所と新しいイメージのpathとnameを指定してあげるだけです。
あとはtifigがよしなにやってくれます。
結果、こんなコマンドが叩かれています。
tifig ***/***/test.heic ***/***/test.jpeg
ここで注意してほしいのは、
const command = `tifig ${oldFilePath} ${newFilePath}`;
ここの部分なのですが、
newFilePathにパイプでつないでshellを叩くことができてしまうので、
しっかりセキュリティをいれてください!!
参考:https://github.com/boutell/heic-to-jpeg-middleware
横向きを縦向きになおす
const sharp = require('sharp');
const fs = require('fs');
class Format {
// 画像の向きを正面にする
async toValidPosition(filePath) {
try {
// 正面画像のBufferを取得
const outputBuffer = await this._getValidPositionImageBuffer(filePath)
// Bufferを元画像に上書きする
fs.writeFileSync(filePath, outputBuffer);
} catch (err) {
throw err;
}
}
_getValidPositionImageBuffer(filePath) {
return new Promise((resolve, reject) => {
sharp(filePath)
.rotate()
.toBuffer((err, outputBuffer) => {
if (err) {
return reject(new Error(err));
}
return resolve(outputBuffer);
});
});
}
}
module.exports = Format;
正面にするのは、これもよしなにsharp.reotate()でやってもらってます。
最後に.toBufferでBufferを返してもらい、
fsでもとのBufferを上書きします。
これで正面向きの画像に変換されます。
まとめ
やっている内容は結構シンプルなんですが、ここまでheicについての調査や画像を正面にするためのライブラリを見つけるのに結構時間がかかりました。。。
tifigじゃなくて、npmで簡単に変換してくれるようなライブラリ出てきてくれないかな。。。