綴Photo の勉強ノートに戻る
コードレビュー学習2026.06.27

「再エンコードすれば位置情報は落ちる」はライブラリ次第で成り立たない — 思い込みをテストで縛る

プライバシーEXIFテスト

自動で回したコードレビューで「設計メモの前提が崩れた」一件を、勉強のためにまとめたものです。

ここで言う「web」は、綴ったものを web でも見られるように開発中のアプリ(紡Book)のことです。

写真日記アプリ(綴Photo)は、写真を web に上げるとき長辺を縮めて JPEG で再エンコードしてから送ります。設計メモには「再エンコードで EXIF(撮影機材・位置情報など)は剥がれている(確認済)」と書いてあった。今回その一文を、テストで裏取りしようとしたら崩れた。ここではその一点に絞って観察する。

この記事で扱う観察ポイント

  • そもそも EXIF と「再エンコード」で何が起きるのか
  • 「再エンコードすれば剥がれる」が、ライブラリ次第で剥がれないこと
  • 「確認済」と書いてある前提を、実際に縛るテストで踏むという発想

便利な「作り直し」は、メタデータについては素通しのこともあります。今回はその一例を見る。


GPS は JPEG ファイルの中。画面にも端末 SQLite にも無い。JPEG ファイル画素(絵)EXIF(GPS・機材・向き)ディスク。閉じても残るDB ではない画面絵だけ見えるGPS は出ない保存しないファイルの中は覗かない端末 SQLite photoslocalPath などGPS の列は無い閉じても残る位置は持たないメモリの Imageデコードのあと、画素と EXIF が両方乗る保存しない。アプリの DB ではない

コードで起きていたこと

アップロードの前処理は、長辺がはみ出していれば縮小し、最後に JPEG で書き出すだけだった。

dart
// 元の構造(要点だけ)
final decoded = decodeImage(input); // 画像を読み込む
final resized = longEdge > 1920 ? copyResize(decoded, ...) : decoded;
return encodeJpg(resized); // もう一度 JPEG として書き出す

「decodeImage で読み込んで encodeJpg で書き直すのだから、付箋(EXIF)は付いてこない」——設計メモの「確認済」はこの直感に乗っている。一般にもそう誤解されやすいです。そこで、それを縛るテストを AI に書いてもらった。位置情報入りの JPEG を作り、前処理に通し、出てきた画像から EXIF が消えていることを確かめる、という内容だ。

dart
// 足したテスト(要点)
final input = jpegWithGps(); // GPS 入りの JPEG を用意
final out = resizeToJpeg(input); // 前処理に通す
expect(exifOf(out).isEmpty, isTrue); // 付箋が消えているはず

このテストが、落ちた。縮小しない小さい画像でも、縮小する大きい画像でも、出力に EXIF が残っていた。

入力 JPEG の GPS は、メモリの Image を経由して出力 JPEG へ戻る。DB には無い。入力 JPEG ファイル画素 + GPSディスク。閉じても残るDB ではないdecodeメモリの Image画素 + EXIF を保持保存しないDB ではないencode出力 JPEG バイト画素 + GPS が残るアップロード用。ファイルの中画面には出ないdecode と encode は交差。置き場ではない。このライブラリでは EXIF が素通しになる。

なぜそうなりやすいか

「再エンコードすれば消える」は、もっともらしいです。実際、消えるライブラリも多いです。だから「確認済」と書いた時点では、たぶん別経路(OS が作るサムネイル)だけを見て確かめ、全経路がそうだと一般化してしまったのだと思う。デスクトップのフルサイズ経路だけが、ライブラリの「メタデータ引き継ぎ」を踏んでいた。

加えて、この穴は普通に使っていても見えません。漏れているかどうかは、出てきたファイルの中身を覗いて初めて分かります。つまり「正常に見える」と「安全」が一致しません。

改善の方向

メタデータを「消えるはず」に任せず、明示的に空にしてから書き出すようにした。コードは AI に直してもらった。要点は「回転を画素に焼いてから消す」順番にある。

dart
// 直した版(要点)
final out = bakeOrientation(resized); // 回転情報を画素そのものへ反映
out.exif = ExifData();                // 付箋を空に差し替える(GPS ごと消える)
return encodeJpg(out);
向きは画素へ焼いてから EXIF を空にする。GPS は出力 JPEG に残らない。メモリの Image画素 + 向き + GPSdecode 直後保存しない焼いて空同じメモリ画素は縦に焼済exif は空保存しないencode出力 JPEG バイト画素だけGPS も向きタグも無いファイルの中。送るバイト焼く前に空にすると、向きタグが消え、縦写真が倒れて見える。焼く・空にする・encode は交差。置き場ではない。

学びポイント

「作り直せば余計なものは落ちる」は、ライブラリ次第で成り立ちません。とくにプライバシーに関わるメタデータは、「落ちているはず」を信じず、明示的に消したうえで、消えていることをテストで縛ります。今回効いたのは、設計メモの「確認済」を文章のまま信じず、コードで踏みにいったことだった。踏んだら前提が崩れていた。


このテーマの括り

この観察は「『たぶん大丈夫』は、コードで踏むまで分からない」という一般則の一例です。

思い込み 実際 縛り方
再エンコードすれば EXIF は落ちる ライブラリがメタデータを引き継ぎ、位置情報が残っていた 明示的に空へ + 消えていることをテストで固定

設計メモに「確認済」と書いた一文ほど、後から誰も疑わなくなります。疑われなくなった前提こそ、テストで踏み直す価値があります。

おわりに

この穴は、「確認済」という言葉を信じて、その横をテストで通していなかったから残っていた。プライバシーに関わる前処理は、画面で正常に見えても安全とは限りません。出てきたファイルの中身を実際に覗くテストを、本体と同じ重さで置いておきたい。

この記事は2026年6月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。