[React] 画像のExif情報を削除する方法
概要
フロント(React)でファイルのExif情報を削除する実装の仕方のメモです。
S3の場合、 S3のイベントでLambda関数を走らせて、関数内で画像のExif情報を削除するといった方法が考えられるが、S3にアップロードする前にフロント側でExif情報を削除しておきたいといった要件も多いと思います。
今回は、JSでExif情報の取得&削除するコードの例を記載します。
Lambda関数(Python)で削除する際の実装例は以下を参考にして頂けたら光栄です。
Exif情報とは? なぜ削除する必要があるのか?
Exif情報とは、Exchangeable Image File Format」(エクスチェンジャブル イメージファイル フォーマット)
の略語で画像のファイル形式のことで、
写真の撮影日時や撮影場所、カメラの機種名、カメラの設定、編集に使ったソフトウェアなどさまざまな情報を含んだデータの集まりのことです。
その画像が、いつ、どこで、どのように撮影されたかが記録されています。
通常、スマホやデジカメなどで撮影したJPEG
ファイルやTIFF
ファイルにはExif情報は含まれており、画像投稿サイトなどでは、ユーザー側でExif情報を削除してからアップロードするか、システム側で自動で削除した方が安全です。
もし削除しない場合、悪意のある利用者によって現在住所などが漏洩するといった可能性が考えられます。
Exif情報を取得する
使ったライブラリ
今回使ったライブラリはblueimp-load-image
です。
blueimp-load-imageはJavaScript でクライアントサイドの画像ファイルを読み込み、操作、および変換するためのライブラリです。
この他に、exif-js
というライブラリもシンプルで使えそうでしたが、以下のようにTypeScriptの型定義が少し甘い気がしたので、今回はblueimp-load-imageを使うことにしました。
interface EXIFStatic {
getData(url: string, callback: any): any;
getTag(img: any, tag: any): any;
getAllTags(img: any): any;
pretty(img: any): string;
readFromBinaryFile(file: any): any;
}
declare var EXIF : EXIFStatic;
export = EXIF;
Exifデータを取得
取得するだけでしたら、かなりシンプルです。
import loadImage from 'blueimp-load-image';
........
........
const getExifData = (file: File) => {
loadImage.parseMetaData(file, (data) => {
console.log(data)
console.log('Exif data: ', data.exif)
})
}
const onChangeFile = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) return
const file = e.target.files[0]
getExifData(file)
}
return (
<div>
<input type="file" onChange={onChangeFile} accept=".jpeg, .jpg, .png, .webp, .gif" />
</div>
)
以下のようにmetaデータが取得されています。
metaデータを解析するには、parseMetaDataオプションを使って、第一引数にFileかBlobかURLを指定して解析することができます。
loadImage.parseMetaData(
file, //fileか、blobか、dataURLか
(data) => {
console.log(data)
console.log('Exif data: ', data.exif)
})
MetaデータおよびExifデータが存在しない場合、空のオブジェクトとなります。
Exifデータを削除
Exifデータの削除は、元ファイルからMetaデータを取り除いたファイルを新しく生成するという流れで実装します。
const removeExifData = (file: File, func: (newFile: File) => void) => {
//削除
loadImage(file, (canvasOrError) => {
if (canvasOrError instanceof Event && canvasOrError.type === 'error') {
console.error('Error loading image:', canvasOrError);
return;
}
const canvas = canvasOrError as HTMLCanvasElement;
canvas.toBlob((blob) => {
if (!blob) return;
const fileWithoutExif = new File([blob], file.name, { type: file.type });
func(fileWithoutExif);
}, file.type);
},{
canvas: true,
orientation: true,
meta: false,
})
}
const onChangeFile = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!e.target.files) return
const file = e.target.files[0]
removeExifData(file, (newFile: File) => {
//本当に削除されているか確認
loadImage.parseMetaData(
newFile,
(data) => {
console.log(data)
console.log('Exif data: ', data.exif)
})
})
}
特に難しいことはしていませんが、
loadImage(file, (canvasOrError) =>
でファイルを解析し、
const canvas = canvasOrError as HTMLCanvasElement;
canvas.toBlob((blob) =>
でBlobに変換してから
const fileWithoutExif = new File([blob], file.name, { type: file.type });
でBlobから新しいファイルオブジェクトを生成しています。
ポイントは、toBlobでBlobに変換する際に、
meta: false,
でmeta情報を引き継がないようにすることです。
無事削除されていることが確認できると思います。
参考
画像のExif情報を読み込むJavaScriptライブラリExif.jsの使い方
Discussion