FileAPIを使用して画像のアップロード・プレビューを実装(ドラッグアンドドロップ対応)

JavaScript

題の通り ファイルのアップロード ▶︎プレビュー表示 をvanillaJSで実装しました

実装したもの

ファイルのアップロードは、ドラッグアンドドロップ またはファイル選択ボタンからできる仕様です

HTML

<input type="file" name="file" id="file">

✏︎ multiple属性を付けると、複数ファイルを指定できる

<input type="file" name="file" id="file" multiple>

✏︎ accept属性で受け入れるファイルのタイプを指定することができる

<input type="file" name="file" id="file" accept=image/*>

JavaScriptで扱う

filesプロパティを使用すると FileLIstオブジェクトへアクセスすることができ、
選択したファイルの情報を確認することができる

file.addEventListener("change",(e) => {
  console.log(e.target.files);
});

検証ツールを見てみると、以下のような内容が確認できました

lastModified … ファイルの最終更新時刻をミリ秒で表す
lastModifiedDate … ファイルの最終更新時刻
name … ファイル名
size … ファイルサイズ(バイト単位)
type … ファイルのタイプ・種類

File - Web API | MDN
File インターフェイスは、ファイルについての情報を提供したり、ウェブページ内の JavaScript からその内容にアクセスできるようにしたりします。

プレビュー表示する

FileRender

データの読み込みに FileRenderオブジェクトを使用しました

FileRenderは非同期のため、イベントを使用して読み取り結果を取得しました
今回は、画像のURLを取得するためにreadAsDataURL()を使用しました

loadはエラーなく読み込みが完了した際に発生し、errorはエラーが発生した際に起こるイベント
loadが起こった際には、readAsDataURL()の結果を、render.result で取得しました

const readerUploadImage = async (file) => {
  const reader = new FileReader();
  return new Promise((resolve, reject) => {

    reader.addEventListener('load', () => {
      resolve(reader.result);
    });

    reader.addEventListener("onerror", () => {
      reject(new Error("ファイルの読み込みに失敗しました"));
    });

    reader.readAsDataURL(file);
  });
}

読み込み結果を取得できた場合の処理と、エラーだった際の処理を定義しました

const handleUploadFile = async (file) => {
  let result;
  try {
   // ファイルの読み込み結果を取得する
    result = await readerUploadImage(file);
  } catch (e) {
    uploadBox.textContent = e;
    return;
  }
 
  //変更するボタンを有効に切り替え
  changeButton.disabled = false;
  //プレビューの表示を実行する
  showPreviewImage(result);
}

// ファイルが選択された際に実行
fileField.addEventListener('change', (e) => {
  const file = e.target.files[0];
  handleUploadFile(file);
});

プレビュー表示するための要素を生成し、
<img>のsrcにはreadAsDataURL()で取得した値を入れ DOMに追加しました

const createPreviewImage = (result) => {
  const img = document.createElement("img");
  const uploadPreviewWrapper = document.createElement("div");
  img.src = result;
  uploadPreviewWrapper.appendChild(img);
  return uploadPreviewWrapper;
}

const showPreviewImage = (uri) => {
  uploadBoxInner.style.display = "none";
  uploadBox.appendChild(createPreviewImage(uri));
}

ドラッグアンドドロップの実装

ドラッグアンドドロップで画像のアップロードができるようにAPIを使用して実装しました

HTML ドラッグ & ドロップ API - Web API | MDN
HTML ドラッグ & ドロップインターフェイスにより、アプリケーションはブラウザーでドラッグ & ドロップ機能を使用することができます。

以下のイベントを使用しました

dragenter … ドラッグ中のファイルが対象の範囲に入った際に発生する
dragover … ドラッグ中のファイルが対象の範囲上にある場合に数ミリ秒間隔で発生する
dragleave … ドラッグ中のファイルが対象範囲を離れた場合に発生する
drop … 対象の範囲にドロップされた際に発生する

classのつけ外し

ドラッグ中のファイルが対象の範囲に入ったことがユーザーに視覚的にわかるように
class名を付与し、styleに変化を与えました

["dragenter", "dragover"].forEach((type) => {
  uploadBox.addEventListener(type, (e) => {
    e.stopPropagation();
    e.preventDefault();
   uploadBox.classList.add("is-drag-over");
  });
});

['dragleave', 'drop'].forEach((type) => {
  uploadBox.addEventListener(type, (e) => {
    e.stopPropagation();
    e.preventDefault();
    uploadBox.classList.remove("is-drag-over");
  });
});
.is-drag-over {
  border: dashed 3px #daf2fc;
}

dataTransfer

dataTransferプロパティを使用してドラッグしたファイル情報を取得しました
dataTransfer.filesはドラッグ操作中のファイルのリストにアクセスすることができるプロパティ

uploadBox.addEventListener('drop', (e) => {
  e.stopPropagation();
  e.preventDefault();
  const file = e.dataTransfer.files[0];
  handleUploadFile(file);
});

あとは先ほど同様の関数を使用してプレビュー表示します

参考サイト

https://developer.mozilla.org/ja/docs/Web/API/File/Using_files_from_web_applications
How To Make A Drag-and-Drop File Uploader With Vanilla JavaScript — Smashing Magazine
File selection inputs are difficult to style the way developers want to, so many simply hide it and create a button that opens the file selection dialog instead...
Using Promises with FileReader
Using `await` to handle FileReader's asynchronicity can be a lot simpler than dealing with events.
Using the HTML5 Drag and Drop API  |  Articles  |  web.dev
The HTML5 Drag and Drop API means that we can make almost any element on our page draggable. In this post we’ll explain the basics of drag and drop.
タイトルとURLをコピーしました