ふんばりフロントエンジニアのブログ

新米フロントエンジニアの備忘録です。ふんばり温泉タオル欲しい...

ローカル画像から直接フォームのファイル(type=file)に画像情報を渡す方法

今回も小ネタです。
あんま必要とする人いないかもしれないですが、タイトルに際した状況に出くわしたのでメモ。

概要

画像プレビュー機能を実装する際には、「input(type=file)」のイベントをハンドリングして画像情報を引き出してプレビュー用の要素に画像を下記のように設定するような流れが多いですよね。

const target = document.getElementById('input_file');
const preview = document.getElementById('preview');

target.addEventListener('change', (e)=>{
        preview.classList.add('drop');

        const FR = new FileReader();
        FR.onload = (e) => {
            preview.querySelector('img').src = e.target.result;
        };
        FR.readAsDataURL(e.target.files[0]);
    });


FileReaderを使って画像のsrcに設定するわけですが、「 e.target.result」には「readAsDataURL」で生成されたbase64形式の文字列が返ってきます。

さて、こんな感じでプレビュー機能を作っていたわけですが、プロフィール画像の変更が不要な時にファイルのプレビュー出すのはいけるけどそのまま送信したら空になるしアカンなと気づいたわけです。

サーバーサイドで処理させてもいいんですが、ちょっとファイルの扱いがまだ勉強不足だったこともあり、フロントで対処しようとなった次第です。

実装コード

const src = thumb.src;
const pos = src.lastIndexOf('.');
if (pos === -1) return '';
const ext = src.slice(pos + 1); // 拡張子取得

fetch(src)
    .then(res => res.blob())
    .then( (blob) => {
        const file = new File([blob], `image.${ext}`);
        let list = new DataTransfer();
        list.items.add(file);

        target.files = list.files;
})

まあやり方があってるのかはわかりませんが..

上の四行目までは拡張子取得してるだけなので気にしなくていいです。

流れとしては、
1. 画像のURLを叩いてBlobに変換
2. blobから新しくFileオブジェクトを作成
3. DataTransferを作成し、そこに作成したFileオブジェクトを追加
4. ファイルinputのfilesにDataTransferを入れる

といった感じです。

まず、画像のsrcを取得し画像の保管場所を取得してそこをfetchで叩きます。

そうするとレスポンスをblobで取得できる「res.blob」を使ってblobに変換します。

blobはバイナリデータが格納されているImmutableなオブジェクトです。生データが入ってるということですね。

そしてFileオブジェクトはblobを引数に新しいファイルを作成することができます。

ファイルinputのfilesはDataTransferオブジェクトだったので、新しくDataTransferを作って渡している、というわけです。

やっぱサーバーサイドで処理すりゃよかったかな..と思いましたが、勉強になりました。