クライアント側(ブラウザ)でサムネイル画像を作る方法

現在とある通販サイトを制作中なのですが、利用するユーザーの本人確認が必要なため、身分証をアップロードするステップがあります。

アップロード画像のサムネイルをどのように生成するかが本日のテーマでございます。

サーバサイドでサムネイル画像を作るのはいろいろと面倒。

一般的には、サーバーサイドで画像処理してサムネイルを生成するのが一般的かと思いますが、実際にやろうと思うとサーバ環境の構築がなかなか面倒なことになります。

ImageMagicなどをインストールする必要がありますし、大きな画像を扱う場合には、メモリの余裕についても考慮する必要があります。頻度は高くなくとも、偶然に複数の処理が重なってサーバが落ちるなんてことも考えられます。

サムネイル画像はクライアント側で楽に作ろう!

メインの機能でもないのに、コストをかけるのはスマートではありませんので、クライアント側(ユーザーのブラウザ上)でサムネイルを作れば良いのではないか?

そこでひらめいたのが、JavaScriptのcanvasを使う方法です。

// デフォルトのサムネイル画像を設定する
const imgIcon = 'data:image/jpeg;base64,/9j/4AAQSkZJR.........'

const makeThumbnail = (file) => {
  // 非同期の処理になるのでPromiseを返す
  return new Promise((resolve) => {
    try {
      // 動的にcanvasを生成する
      const canvas = document.createElement('canvas')
      canvas.width = 24
      canvas.height = 24

      // canvasのコンテキストを取得しておく
      const ctx = canvas.getContext('2d')

      // FileReaderを生成する
      const reader = new FileReader()

      // ファイルを読み込む
      reader.readAsDataURL(file)

      // 読み込みが完了した際の処理
      reader.onload = () => {
        // Imageオブジェクトを生成する
        const img = new Image()

        // Imageオブジェクトに読みこんだデータを渡す
        img.src = reader.result

        // Imageの読み込みが完了した際の処理
        img.onload = () => {
          // コンテキストにImageオブジェクトの画像を描画する
          ctx.drawImage(img, 0, 0, 24, 24)

          // canvasの描画結果をDataURL形式で取得して返却する
          resolve(canvas.toDataURL('image/jpeg', 0.5))
        }

        // エラー処理
        img.onerror = () => {
          throw new Error('error')
        }
      }
    } catch (err) {
      // 処理に失敗したらデフォルトサムネイルを返す
      resolve(imgIcon)
    }
  })
}

imgIcon には、処理に失敗した際に表示するデフォルトのサムネイル画像をbase64エンコードした文字列を代入しておきます。

makeThumbnail関数をファイルオブジェクトを引数に与えて呼び出すと、base64エンコードされたサムネイル画像が生成されます。Promiseを使っていますので、async await を使うか、thenを使ってサムネイルデータを受け取ってください。

処理のおおまかな流れは以下のとおりです。

  1. 動的に canvas を生成
  2. HTML5のFileReaderを使ってファイルデータを読み込む
  3. imgオブジェクトを動的に生成し、ファイルデータを読み込ませる
  4. canvasのcontextにimgを描画
  5. canvasからDataURLを取得

今回は、ざっくりどんな画像かが分かれば十分なので、画像のクリッピングなどはせず強制的に正方形に変形させています。もし気になる方は、ctx.drawImageの座標を適宜書き換えてください。

サーバ側で処理するには、かなりの工数をかけてサーバ環境を構築する必要がありますが、クライアント側で処理させれば、たったこれだけのコードで済んでしまいます。かなりお勧めの方法です。

※canvasで扱える画像形式(jpeg, gif, pngあたり)にしか対応しませんので、例えばTiffなどの場合はデフォルトのサムネイルを返します。

余談ですが、小さなサムネイル画像とはいえ、本人確認書類ですので、外部からアクセス可能な場所に置いておくのは避けたいところです。今回はアカウントのテーブルにサムネイル用のフィールドを作って、文字列化されたサムネイルデータをそのまま格納してしまおうと思います。たいした文字数ではないし、jsonで扱えるのでとても便利です。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする