🌇

ブラウザ上での画像変換で扱うオブジェクトとその変換(TypeScript)

2022/07/05に公開

はじめに

ブラウザ上で画像を扱う際に、扱うオブジェクトが複数あり、その全体を把握・整理するためのドキュメントです。

何ができるようになる?

  • ブラウザ上での画像変換用に扱うオブジェクトの概要
  • それぞれのオブジェクトの生成方法
  • 画像形式の変換
    • jpeg→png / gif→png など
  • 画像のリサイズ

本記事での対象としないこと

  • PDFファイルの扱い

ブラウザでの画像変換で扱うオブジェクト一覧

まずは、それぞれ簡単に解説していきます

扱うオブジェクトの紹介

File

  • 通常、HTMLのinput要素を経由して、ファイルを選択した結果として返されます。
  • 画像とは限りません。
  • インスタンスメソッドにより、最終更新日/ファイル名/MIMEタイプ/サイズ(byte)を読み取れます。
MIMEタイプとは

MDN: https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types によると

文書、ファイル、またはバイト列の性質や形式を示す標準

一般的には、ファイルの拡張子に対応する。

ObjectURL

  • Fileオブジェクトを識別できるURLのことです

Fileオブジェクトがブラウザに保存されていて、それを参照できるURL=ObjectURLです

const objectURL = URL.createObjectURL(file); // fileはFileオブジェクト

とすることでObjectURLを作成できます。

DataURL

data:[<mediatype>][;base64],<data>の形式で、小さなファイルをインラインで文書に埋め込むことができ、ObjectURLとは異なり、そのURL自体で、ファイルを表現できます。

例えば、以下のDataURLをブラウザで表示できる

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAA1klEQVRIie2WsQ3CMBBFXxBjJKPQE9GxDCWhZCCgpmKPJD0jhAJFODaOHdsHjb90zTm69/9FsgxZWT/QDuiBQag6oP4G7gShY7UjrFDAw8INhaoAWAkNvwAlUAE318cpV1oqcyvtDLAnPnkmSyLdVUN44ivTVRuJ58AAhwi4rbzANvgxwpQ3GKZrf1j6ImAdcnbAmxlTi8Eq5Dlj6u4wFQQG2AObQFNR4BBTxmzbXa32U8iYLXVXO/U38NrSj/nPXlIT99Iw3o8NQzWyr5AW2AoFysr66AWhSOt0gsDT9AAAAABJRU5ErkJggg==

HTMLImageElement

HTMLの<img>に等しい。
JavaScript上では、document.createElement('img')またはnew Image()でオブジェクトを生成できます。
画像変換においては主にsrc属性onloadイベントリスナーを用います。
画像変換においては、HTMLImageElementをブラウザに表示する必要はなく、処理の過程のみで使うことが可能です。

canvas/drawImage

canvasはブラウザ上でグラフィックを描画できます。
HTMLImageElementをcanvasでdrawImageでき、このときに画像のリサイズやクリップも可能です。
drawImageした後、toDataURLというメソッドを使うことで、DataURLを取得できます。
画像変換においては、canvasをブラウザに表示する必要はなく、処理の過程のみで使うことが可能です。

関係性

関係性は以下のようになっています。
それぞれの変換関数の実装は次項です。

それぞれの変換関数の実装

TypeScriptで実装しています。

1. URL.createObjectURL

const objectURL = URL.createObjectURL(file); // fileはFileオブジェクト

2. cvtObjURL2Image

async関数にです。
Promiseを用いて、画像がロード終了後のHTMLImageElementを取得します。

async function cvtObjURLToImage(
  objURL: ReturnType<typeof URL.createObjectURL>
) {
  return new Promise<HTMLImageElement>((resolve, reject) => {
    const img = new Image();
    img.onerror = (e) => {
      console.error("failed to load image from objUrl");
      reject(e);
    };
    img.onload = () => {
      resolve(img);
    };
    img.src = objURL;
  });
}

3. cvtHTMLImageElement2Canvas

下記の例では、widthとheightを指定することで、画像のリサイズが可能です。

function cvtHTMLImageElement2Canvas(
  img: HTMLImageElement,
  width: number,
  height: number,
) {
  const canvas = document.createElement("canvas");
  canvas.width = width;
  canvas.height = height;
  const ctx = canvas.getContext("2d");
  if (!ctx) {
    throw new Error("failed to getContext from canvas");
  }
  ctx.drawImage(img, 0, 0, width, height);
  return canvas
}

4. canvas.toDataURL

3. cvtHTMLImageElement2Canvasの返り値に対してtoDataURLを実行するだけ。MIMEタイプを指定できます。

const dataURL = canvas.toDataURL("image/png");

5. cvtDataURL2File

ファイル名を指定する必要があります。
dataURLをfetch後、blobに変換し、Fileオブジェクトを生成します。
async関数です。

async function cvtDataURL2File(
  dataURL: string,
  filename: string
): Promise<File> {
  const blob = await (await fetch(dataURL)).blob();
  return new File([blob], filename);
}

まとめ

前項の関数を組み合わせることで、HTMLのinput要素から入力されたFileオブジェクトを変換し、リサイズして、新たにFileオブジェクトを生成することができます。

前項で触れていませんが、dataURLをHTMLImageElementに変換することも可能です。

ブラウザでの画像変換は、様々なオブジェクトを経由するので、全体を理解していないと、複雑なコードになったり、余計な処理を挟んでしまいがちかと思います。

最小の手数で画像を変換できるように、これらのオブジェクトの関係性を把握しておきましょう。

Discussion