猫の手ツール

Step 3: Core Technology

15. Blob と File の違い:
「画像データ」の正体を掴む

Fileはファイル名などの情報を持つBlobです。画像変換では、最初のFileと、変換後のBlobを分け、保存名は別に管理します。Canvasへ描いた時点でBlobになるわけではないので、入力・画素・書き出し結果の順に確認しましょう。

1. Blob とは「不変な生のデータの塊」

Blob (Binary Large Object) は、その名の通り「巨大なバイナリデータの塊」を指します。中身がテキストであれ画像であれ、コンピュータにとっては単なる 0 と 1 の羅列に過ぎません。

重要なのは、Blobは 不変(Immutable) であるという点です。一度作成されたBlobの中身を直接書き換えることはできません。Web開発においては、Canvasから画像を生成したり、データベース(IndexedDBなど)に一時的なデータを保存したりする際に、この「生のデータ」という形式が重宝されます。

2. File とは「名札(メタデータ)が付いた Blob」

一方、File インターフェースは、Blobを継承(extend)した特別なオブジェクトです。簡単に言うと、「Blob + 名前や最終更新日時」 です。

size と type はBlobにもあります。Fileで追加される代表的な情報は name と lastModified です。また new File() で作成したFileは、OS上に実ファイルが存在することを意味しません。ダウンロードや保存の処理は別途必要です。

コードで見る生成元による違い

【File】ユーザーが選択したファイル(名札あり)

// input type="file" 要素から取得する場合
const file = inputElement.files?.[0];
if (!file) throw new Error('ファイルを選択してください');

console.log(file.name); // "profile.jpg"
console.log(file.lastModified); // 1673456789000
console.log(file instanceof Blob); // true (FileはBlobの一種)

【Blob】Canvasやプログラムで生成したデータ(名札なし)

// Canvasから画像を生成する場合
canvas.toBlob((blob) => {
  if (!blob) { console.error('画像を生成できませんでした'); return; }
  console.log(blob.size); // データサイズはわかる
  console.log(blob.type); // "image/png"
  // console.log(blob.name); // ⚠️ undefined (名前はない)
}, "image/png");

3. 注意点:File と Blob の相互変換

フロントエンドの実務において、「Blobを受け取る関数にFileを渡す」のは問題ありませんが、その逆は工夫が必要です。

  • File は Blob の子: 多くのAPI(URL.createObjectURL や FileReader)は、引数に Blob を要求しますが、File を渡しても動作します。File は Blob の性質をすべて持っているからです。
  • Blob を File に変換する: キャンバスで加工した Blob を、サーバーへ「ファイル」として送信したい場合があります。その際は new File() で名前を付ける方法があります。FormData.append() にBlobとファイル名を渡す方法もあり、送信のために必ずFileへ変換する必要はありません。
// myBlob は toBlob(..., "image/png") で作成したPNGのBlob
const fileFromBlob = new File([myBlob], "processed_image.png", {
  type: myBlob.type,
});

4. 実務での活用:猫の手ツール開発での気づき

「猫の手ツール」では、この違いを厳密に使い分けています。

ユーザーがアップロードした瞬間は File として扱い、元のファイル名を引き継ぎます。しかし、Canvasに描画した内容はピクセルとして保持されます。toBlob() で書き出すとBlobになり、toDataURL() で書き出すと文字列になります。描画した時点で自動的にBlobへ変わるわけではありません。

保存名はnew File()で与える方法と、保存リンクのdownloadに指定する方法があります。どちらも中身の再エンコードとは別の処理です。

拡張子やMIMEタイプを書き換えても画像形式は変わらない

PNGのBlobを new File() で包み、名前を .jpg、typeを image/jpeg にしても、中身はPNGのままです。形式を変えるには、画像をデコードし、CanvasのtoBlobなどで目的の形式へ再エンコードします。

前掲のサンプルでは inputElement は既存のファイル入力要素、canvas は描画済みCanvasを指します。保存後の拡張子だけで判断せず、生成したBlobのtypeと、書き出しAPIへ指定した形式を照合すると、形式違いの不具合を追いやすくなります。

画像変換は、画像データと保存名を別々に引き継ぐ

画像変換ツールは、元のFileから名前を取り、加工対象をprocessBlobへ保持します。FileはBlobとして渡せるため、通常画像なら元のFileをそのまま使い、HEIC変換後は生成したBlobへ差し替えます。

const originalName = file.name;
const baseName = originalName.substring(0, originalName.lastIndexOf('.')) || originalName;
let processBlob = file;
// 変換が必要ならprocessBlobを生成されたBlobへ差し替える。
// 最終出力ではshowResult(convertedBlob, baseName)へ別々に渡す。

本番処理の型を省いた抜粋です。showResult()が保存リンクのdownloadに名前を指定するので、Blob自体にnameを追加する必要はありません。制作解説と保存名の組み立てへ進み、形式を変える処理と拡張子を付ける処理を分けて確認できます。

まとめ

「Blobは中身のデータ」「Fileは名前付きの器」。

データが生成された場所と、名前・形式を指定する場所を分けて読むと、保存時の形式違いを切り分けやすくなります。

参考資料