[JavaScript] 動的にinput[type=file]を組み立てて画像をアップロードしたらBase64が手に入るコード


画像アップロードする時に、以下のように display: none をつかってブラウザ本来の input[type=file] をみえないようにした上で独自レイアウトのボタンで画像アップロードするような実装を時々みかけます。

<label for="hoge">画像アップロード</label>
<input id="hoge" type="file" style="display: none">

で、 addEventListener でファイル変更検知を行ったりと。
けれど、上記のようにするとマークアップがある程度拘束されてしまいます。やりたいことはボタンをクリックしたら画像をアップロードしてBase64を手にいれるとかそういう話なので、クリックイベントを仕込んで以下のコードを実行すればOKです。

<div style="width: 0; height: 0; overflow: hidden;">
  <input id="browserPhotoUploader" type="file" accept="image/*" />
</div>
public getPictureFromBrowser() {
  /*
   * input[type=file] を組み立て
   */
  const inputFile: HTMLElement = document.querySelector('input#browserPhotoUploader');

  return new Promise(resolve => {
    /*
     * input[type=file] の変更(change)を検知したら、ファイルをアップロードしてresolveする
     */  
    inputFile.addEventListener(
        'change',
        (e) => {
          const file = e.target.files[0];
          const reader = new FileReader();

          reader.onload = (fileInput => {
            /*
             * アップロードされるファイルが画像かを簡易判定
             */  
            if (file.type.indexOf('image') < 0) {
              reject(null);
            }

            return (event) => {
              resolve(event.target.result);
            };
          })(file);

          reader.readAsDataURL(file);
        },
        false,
    );

    /*
     * input[type=file] をクリック
     * イベントリスナーよりあとに実行されるように
     */  
    inputFile.click();
  });
}

このコードだったらHTML側は

<button (click)="getPictureFromBrowser()">画像アップロード</button>

だけで済みますね。 display:none せずにすむのでシンプル!

それでは、また。

おまけ: TypeScript版

any 使ってるのちょっと何ですが・・・。

public getPictureFromBrowser(): Promise<string> {
    const inputFile: HTMLElement = document.querySelector('input#browserPhotoUploader');
    return new Promise(resolve => {
      inputFile.addEventListener(
        'change',
        (e: any) => {
          const file = e.target.files[0];
          const reader = new FileReader();

          reader.onload = (fileInput => {
            if (file.type.indexOf('image') < 0) {
              reject(null);
            }

            return (event) => {
              resolve(event.target.result);
            };
          })(file);

          reader.readAsDataURL(file);
        },
        false,
      );
      inputFile.click();
    });
  }