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は名前付きの器」。
データが生成された場所と、名前・形式を指定する場所を分けて読むと、保存時の形式違いを切り分けやすくなります。