📷

[React] 画像のExif情報を削除する方法

2023/03/27に公開

概要

フロント(React)でファイルのExif情報を削除する実装の仕方のメモです。
S3の場合、 S3のイベントでLambda関数を走らせて、関数内で画像のExif情報を削除するといった方法が考えられるが、S3にアップロードする前にフロント側でExif情報を削除しておきたいといった要件も多いと思います。

今回は、JSでExif情報の取得&削除するコードの例を記載します。
Lambda関数(Python)で削除する際の実装例は以下を参考にして頂けたら光栄です。
https://zenn.dev/reds/articles/2580aa9d2e33ae

Exif情報とは? なぜ削除する必要があるのか?

Exif情報とは、Exchangeable Image File Format」(エクスチェンジャブル イメージファイル フォーマット)の略語で画像のファイル形式のことで、
写真の撮影日時や撮影場所、カメラの機種名、カメラの設定、編集に使ったソフトウェアなどさまざまな情報を含んだデータの集まりのことです。
その画像が、いつ、どこで、どのように撮影されたかが記録されています。
通常、スマホやデジカメなどで撮影したJPEGファイルやTIFFファイルにはExif情報は含まれており、画像投稿サイトなどでは、ユーザー側でExif情報を削除してからアップロードするか、システム側で自動で削除した方が安全です。

もし削除しない場合、悪意のある利用者によって現在住所などが漏洩するといった可能性が考えられます。

Exif情報を取得する

使ったライブラリ

今回使ったライブラリはblueimp-load-imageです。
blueimp-load-imageはJavaScript でクライアントサイドの画像ファイルを読み込み、操作、および変換するためのライブラリです。
https://github.com/blueimp/JavaScript-Load-Image#meta

この他に、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;

https://github.com/exif-js/exif-js

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の使い方

写真に自動登録される「Exif情報」って何? 知っておくべき安全な使い方

next.jsで画像圧縮のblueimp-load-imageしてみる

Discussion