分析に使用する画像データをログ出力しないようにObjectの非破壊処理がしたい

2021.08.24

こんにちは、CX事業本部 IoT事業部の若槻です。

今回は、分析に使用する画像データをログ出力しないようにObjectの非破壊処理がしたい、という話です。

こんな構成の画像データ分析処理がある

下記のようにアプリケーションから送信された画像データに対して分析処理を実施するAWS Lambda関数があります。

handler.ts

import * as AWSLambda from 'aws-lambda';

const handler = async (event: AWSLambda.APIGatewayEvent) => {
  console.log(event.body); //リクエストボディをログ出力

  if (!event.body) {
    throw new Error('Error');
  }

  const body = JSON.parse(event.body)

  // 画像を分析する処理
  // await analyzeImage(body)

  return;
};

イベントとして渡されるリクエストボディに画像データが含まれます。

リクエストボディ

{
  deviceId: 'デバイス1',
  timestamp: 1629813305,
  image: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'
}

全体構成イメージ

リクエストボディのログ出力に画像を含めたくない

さて、人物の顔写真などプライバシーに関わる画像を分析するシステムでは、分析を実施した後は画像の破棄が必要となる場合が多いです。

今回のシステムでも分析後の画像はシステム内に残さないように破棄する方針となりました。よってLambda関数の冒頭で行っているconsole.log()でのリクエストボディのログ出力にも画像データを含めないようにしたいです。

そのためログ出力されるオブジェクトは、リクエストボディからimageプロパティを削除した下記のようなオブジェクトとしたいです。

出力ログ

{
  deviceId: 'デバイス1',
  timestamp: 1629813305,
}

よって下記のように処理によってimageプロパティの有無を分ける必要が出てきます。

  • 出力ログとするオブジェクトにはimageプロパティを含めない
  • 画像を分析する処理の引数とするオブジェクトにはimageプロパティを含める

delete演算子ではコピー元オブジェクトへも影響が及んでしまう

JavaScriptでオブジェクトからプロパティを削除する場合はdelete演算子を使用します。

delete演算子は破壊処理となるため、出力ログとするオブジェクトに対してのみ実行する必要があります。そこで下記のようにオブジェクトを別の変数にコピーし、コピー先のオブジェクトに対してdeleteを実行してみましたが、コピー元のオブジェクトまでimageプロパティが削除されてしまいました。

>const body = {
...   deviceId: 'デバイス1',
...   timestamp: 1629813305,
...   image: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'
... }
undefined

> logBody = body
{
  deviceId: 'デバイス1',
  timestamp: 1629813305,
  image: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'
}

// コピー先オブジェクトからimageプロパティを削除
> delete logBody.image
true

// コピー先オブジェクト。imageプロパティが削除されている
> logBody
{ deviceId: 'デバイス1', timestamp: 1629813305 }

// コピー元オブジェクト。こちらもimageプロパティが削除されている
> body
{ deviceId: 'デバイス1', timestamp: 1629813305 }

これは、コピー先オブジェクトの実体はコピー元オブジェクトの参照となっているシャロウコピーという状態となっているため、コピー先への破壊処理がコピー元へも及んでしまうとのことです。

ディープコピーをすればコピー元に影響が及ばない

ディープコピー(ディープクローン)をすることにより、破壊処理がコピー元に及ばなくなるとのことなので試してみます。

ディープコピーはJSON.parse()(およびJSON.stringify())による変換を挟んでコピーをすることにより行えます。

> const body = {
...   deviceId: 'デバイス1',
...   timestamp: 1629813305,
...   image: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'
... }
undefined

// ディープコピー
> logBody = JSON.parse(JSON.stringify(body))
{
  deviceId: 'デバイス1',
  timestamp: 1629813305,
  image: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'
}

// コピー先オブジェクトからimageプロパティを削除
> delete logBody.image
true

// コピー先オブジェクト。imageプロパティが削除されている
> logBody
{ deviceId: 'デバイス1', timestamp: 1629813305 }

// コピー元オブジェクト。imageプロパティは残っている
> body
{
  deviceId: 'デバイス1',
  timestamp: 1629813305,
  image: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/...'
}

確かにコピー元オブジェクトへはdeleteの影響が及ばず、imageプロパティが削除されずに残りました。

最終的なコード

下記が最終的なコードです。ディープコピーによりコピー元のオブジェクトを破壊しないようにすることにより、分析に使用する画像データをログ出力しないようにすることができました。

const handler = async (event: AWSLambda.APIGatewayEvent) => {
  if (!event.body) {
    throw new Error('Error');
  }

  const logBody = JSON.parse(event.body); // ディープコピー
  delete logBody.image
  console.log(event.body); // リクエストボディをログ出力

  const body = JSON.parse(event.body);

  // 画像を分析する処理
  // await analyzeImage(body)

  return;
};

参考

以上