Reactでのファイル入力(onChange)とアップロードの完全ガイド

Reactアプリケーションで、ユーザーが画像やドキュメントなどのファイルを選択し、それをアップロードする機能は非常に一般的です。これを実現するには、<input type="file" />要素とReactのイベント処理を組み合わせる必要があります。本記事では、onChangeイベントを使用して選択されたファイルを取得し、サーバーにアップロードするまでの流れを初心者にもわかりやすく解説します。また、ファイルデータのプレビュー表示やエラーハンドリング、複数ファイルのアップロードにも対応した実装例を紹介します。これにより、ファイル処理を伴うReactアプリケーションの構築に自信を持てるようになるでしょう。

目次
  1. ファイル入力(onChange)イベントとは
    1. onChangeイベントの仕組み
    2. 基本的な使用例
    3. onChangeイベントのポイント
  2. ファイル入力フォームの作成
    1. 基本的なファイル入力フォーム
    2. ファイル入力フォームの状態管理
    3. カスタマイズされたファイル入力フォーム
    4. ポイント
  3. ファイルデータの取得方法
    1. イベントオブジェクトを使用したファイル情報の取得
    2. 取得できるファイル情報
    3. 複数ファイルの処理
    4. ファイルデータの状態管理
    5. ポイント
  4. ファイルデータのプレビュー表示
    1. 画像ファイルのプレビュー表示
    2. テキストファイルのプレビュー表示
    3. 複数ファイルのプレビュー表示
    4. ポイント
  5. ファイルのアップロード処理
    1. 基本的なアップロード処理
    2. コードのポイント
    3. 複数ファイルのアップロード
    4. 進捗表示を追加する
    5. ポイント
  6. エラーハンドリング
    1. エラーの種類
    2. エラーハンドリングの基本実装
    3. エラーの種類ごとの対処法
    4. エラー通知のユーザー体験向上
    5. ポイント
  7. 複数ファイルアップロードの実装
    1. 基本的な複数ファイルアップロードの実装
    2. コードのポイント
    3. ファイルプレビュー付きの複数アップロード
    4. コードの改善ポイント
    5. サーバー側の設定例
    6. ポイント
  8. アップロードされたデータの管理
    1. アップロード後のデータ確認
    2. 状態管理のポイント
    3. アップロードファイルのストレージと表示
    4. メタデータの保存
    5. ポイント
  9. まとめ

ファイル入力(onChange)イベントとは


Reactでファイル入力を処理する際に重要な役割を果たすのがonChangeイベントです。このイベントは、ユーザーがファイルを選択したタイミングで発生し、選択されたファイルに関する情報を取得するためのトリガーとなります。

onChangeイベントの仕組み


ファイル入力フォームでは、<input type="file" />要素にonChange属性を指定することで、ファイル選択後の処理をカスタマイズできます。イベントハンドラーは、eventオブジェクトを引数として受け取り、その中のevent.target.filesプロパティを使ってファイル情報を取得します。

基本的な使用例


以下は、onChangeイベントを利用してファイル名を取得する簡単な例です。

import React from "react";

function FileInputExample() {
  const handleFileChange = (event) => {
    const files = event.target.files; // ファイル情報を取得
    console.log("Selected file:", files[0].name);
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
    </div>
  );
}

export default FileInputExample;

onChangeイベントのポイント

  1. 複数ファイルの対応: multiple属性を<input type="file" />に追加することで、複数ファイルの選択が可能になります。選択されたファイルはFileListオブジェクトとして取得されます。
  2. ファイル情報の詳細: event.target.files[0]で取得できるファイルオブジェクトには、ファイル名(name)、サイズ(size)、種類(type)などの詳細情報が含まれます。
  3. Reactでの状態管理: 取得したファイルデータをReactのuseStateフックで管理することで、アップロード処理やプレビュー表示に利用できます。

onChangeイベントは、Reactでのファイル操作の基礎となる重要な機能です。これを理解することで、ファイル入力に関するより高度な操作を実現できるようになります。

ファイル入力フォームの作成

Reactでファイル入力フォームを作成するには、HTMLの<input type="file" />要素をReactコンポーネント内に組み込むだけで簡単に実現できます。ただし、実際のアプリケーションでは、選択したファイルを状態で管理したり、スタイリングを加えることが重要です。

基本的なファイル入力フォーム


以下は、最も基本的なファイル入力フォームの例です。

import React from "react";

function SimpleFileInput() {
  return (
    <div>
      <label htmlFor="file-upload">ファイルを選択:</label>
      <input id="file-upload" type="file" />
    </div>
  );
}

export default SimpleFileInput;

この例では、<label>要素を使用してファイル選択ボタンにラベルを付けています。htmlFor属性でinput要素のidを指定することで、アクセシビリティが向上します。

ファイル入力フォームの状態管理


ファイル選択時にその情報を取得し、ReactのuseStateフックを使って状態管理を行う例です。

import React, { useState } from "react";

function FileInputWithState() {
  const [fileName, setFileName] = useState("");

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file) {
      setFileName(file.name);
    } else {
      setFileName("");
    }
  };

  return (
    <div>
      <label htmlFor="file-upload">ファイルを選択:</label>
      <input id="file-upload" type="file" onChange={handleFileChange} />
      <p>選択されたファイル: {fileName || "なし"}</p>
    </div>
  );
}

export default FileInputWithState;

カスタマイズされたファイル入力フォーム


ブラウザデフォルトのファイル入力ボタンはカスタマイズが難しいため、CSSやJavaScriptで独自のデザインを適用することが一般的です。

import React, { useState } from "react";

function CustomFileInput() {
  const [fileName, setFileName] = useState("");

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file) {
      setFileName(file.name);
    } else {
      setFileName("");
    }
  };

  return (
    <div>
      <label htmlFor="file-upload" style={{ cursor: "pointer", color: "blue" }}>
        カスタムボタン: ファイルを選択
      </label>
      <input
        id="file-upload"
        type="file"
        style={{ display: "none" }}
        onChange={handleFileChange}
      />
      <p>選択されたファイル: {fileName || "なし"}</p>
    </div>
  );
}

export default CustomFileInput;

ここでは、<input>要素を非表示にし、クリック可能な<label>要素を使ってユーザーに操作させることで、見た目をカスタマイズしています。

ポイント

  1. アクセシビリティの向上: label要素を使用して視覚的にも操作性の高いフォームを作成します。
  2. 状態管理: useStateを使って選択されたファイルを管理し、後続の処理に活用します。
  3. デザインの柔軟性: CSSを活用して、アプリのデザインに合わせた入力フォームを作成できます。

これで基本的なファイル入力フォームとその拡張が実現できます。次に、選択したファイルデータの取得方法を詳しく見ていきます。

ファイルデータの取得方法

Reactでファイル入力を処理する際、ユーザーが選択したファイルデータを取得する方法を理解することが重要です。onChangeイベントとイベントオブジェクトを活用することで、選択されたファイルに関する詳細な情報を取得できます。

イベントオブジェクトを使用したファイル情報の取得


onChangeイベントが発生すると、Reactはイベントオブジェクトを提供します。このオブジェクトには、選択されたファイルの情報が含まれており、event.target.filesを使用してアクセスできます。filesFileListオブジェクトで、選択されたすべてのファイルを配列形式で管理します。

以下は、ファイル名を取得する基本的な例です。

import React from "react";

function FileDataExample() {
  const handleFileChange = (event) => {
    const file = event.target.files[0]; // 最初のファイルを取得
    if (file) {
      console.log("ファイル名:", file.name);
      console.log("ファイルタイプ:", file.type);
      console.log("ファイルサイズ:", file.size, "bytes");
    }
  };

  return (
    <div>
      <label htmlFor="file-input">ファイルを選択:</label>
      <input id="file-input" type="file" onChange={handleFileChange} />
    </div>
  );
}

export default FileDataExample;

取得できるファイル情報


ファイルオブジェクトには以下のようなプロパティが含まれます。

  • name: ファイル名 (例: example.jpg)
  • type: MIMEタイプ (例: image/jpeg)
  • size: ファイルサイズ (バイト単位)
  • lastModified: 最終変更日時 (UNIXタイムスタンプ形式)
  • webkitRelativePath: ファイルの相対パス(ディレクトリアップロード時のみ)

複数ファイルの処理


multiple属性を使用すると、複数のファイルを選択可能になります。この場合、filesプロパティはすべての選択されたファイルを含むFileListオブジェクトとなります。

import React from "react";

function MultiFileExample() {
  const handleFileChange = (event) => {
    const files = event.target.files;
    for (let i = 0; i < files.length; i++) {
      console.log(`ファイル ${i + 1}:`, files[i].name);
    }
  };

  return (
    <div>
      <label htmlFor="multi-file-input">複数のファイルを選択:</label>
      <input id="multi-file-input" type="file" multiple onChange={handleFileChange} />
    </div>
  );
}

export default MultiFileExample;

ファイルデータの状態管理


取得したファイル情報をReactのuseStateフックで管理し、後続の処理に利用できます。

import React, { useState } from "react";

function FileStateExample() {
  const [selectedFiles, setSelectedFiles] = useState([]);

  const handleFileChange = (event) => {
    setSelectedFiles([...event.target.files]);
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileChange} />
      <ul>
        {selectedFiles.map((file, index) => (
          <li key={index}>
            {file.name} ({(file.size / 1024).toFixed(2)} KB)
          </li>
        ))}
      </ul>
    </div>
  );
}

export default FileStateExample;

ポイント

  1. event.target.filesの活用: ファイル情報を効率的に取得し、詳細なプロパティにアクセス可能。
  2. 複数ファイルの処理: 配列操作を利用して、選択されたすべてのファイルを扱う。
  3. 状態管理の徹底: 取得したデータを状態で保持し、他のコンポーネントやロジックに引き継ぐ。

これらを活用することで、選択されたファイルデータを適切に取得し、次のアップロードやプレビュー処理へつなげる準備が整います。

ファイルデータのプレビュー表示

Reactアプリケーションでファイルアップロードを行う際、ユーザーに選択したファイルのプレビューを表示することは、操作性やユーザー体験の向上につながります。特に画像やテキストファイルの場合、アップロード前に内容を確認できる機能は重要です。

画像ファイルのプレビュー表示


画像ファイルのプレビューには、FileReader APIを利用します。FileReaderを使うことで、選択したファイルをデータURL形式で取得し、<img>要素に表示できます。

以下は、画像プレビューを実装する例です。

import React, { useState } from "react";

function ImagePreview() {
  const [imageSrc, setImageSrc] = useState(null);

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file && file.type.startsWith("image/")) {
      const reader = new FileReader();
      reader.onload = () => {
        setImageSrc(reader.result); // データURLを状態に保存
      };
      reader.readAsDataURL(file);
    } else {
      setImageSrc(null);
    }
  };

  return (
    <div>
      <input type="file" accept="image/*" onChange={handleFileChange} />
      {imageSrc ? <img src={imageSrc} alt="プレビュー" style={{ maxWidth: "100%", maxHeight: "300px" }} /> : <p>画像を選択してください</p>}
    </div>
  );
}

export default ImagePreview;

このコードでは、accept="image/*"属性を使用して画像ファイルのみに制限しています。また、選択されたファイルが画像以外の場合はプレビューを無効にしています。

テキストファイルのプレビュー表示


テキストファイルの場合、同じくFileReaderを利用してファイルの内容を読み取り、画面に表示します。

import React, { useState } from "react";

function TextPreview() {
  const [textContent, setTextContent] = useState("");

  const handleFileChange = (event) => {
    const file = event.target.files[0];
    if (file && file.type === "text/plain") {
      const reader = new FileReader();
      reader.onload = () => {
        setTextContent(reader.result); // ファイル内容を状態に保存
      };
      reader.readAsText(file);
    } else {
      setTextContent("対応していないファイル形式です。");
    }
  };

  return (
    <div>
      <input type="file" accept=".txt" onChange={handleFileChange} />
      <div>
        <h3>プレビュー:</h3>
        <pre>{textContent}</pre>
      </div>
    </div>
  );
}

export default TextPreview;

ここでは、accept=".txt"を使用して、選択可能なファイルをテキストファイルに限定しています。<pre>タグを使うことでテキストのフォーマットを維持しつつ表示しています。

複数ファイルのプレビュー表示


複数のファイルをプレビューする場合も、FileReaderを利用して、全てのファイルを処理するロジックを作成します。

import React, { useState } from "react";

function MultiImagePreview() {
  const [imagePreviews, setImagePreviews] = useState([]);

  const handleFileChange = (event) => {
    const files = event.target.files;
    const previews = [];

    Array.from(files).forEach((file) => {
      if (file.type.startsWith("image/")) {
        const reader = new FileReader();
        reader.onload = () => {
          previews.push(reader.result);
          if (previews.length === files.length) {
            setImagePreviews(previews); // 全てのプレビューを保存
          }
        };
        reader.readAsDataURL(file);
      }
    });
  };

  return (
    <div>
      <input type="file" accept="image/*" multiple onChange={handleFileChange} />
      <div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}>
        {imagePreviews.map((src, index) => (
          <img key={index} src={src} alt={`プレビュー ${index + 1}`} style={{ maxWidth: "100px", maxHeight: "100px" }} />
        ))}
      </div>
    </div>
  );
}

export default MultiImagePreview;

この例では、選択されたすべての画像ファイルをプレビューとして表示します。

ポイント

  1. FileReaderの活用: ファイル内容を簡単に読み取り、データURLやテキスト形式で扱う。
  2. 状態管理: プレビュー内容をuseStateで管理し、動的に表示する。
  3. UX向上: ファイルが選択された時点でリアルタイムに内容を表示することで、ユーザー体験を改善する。

これにより、ユーザーがアップロードする内容を事前に確認できる機能を実現できます。次は、ファイルを実際にアップロードする方法を見ていきます。

ファイルのアップロード処理

Reactでファイルをアップロードするには、選択されたファイルをサーバーに送信するロジックを実装する必要があります。一般的には、HTTPリクエスト(主にPOST)を使用し、FormDataオブジェクトを利用してファイルデータを送信します。

基本的なアップロード処理


以下は、ファイルをサーバーにアップロードする最も基本的な例です。

import React, { useState } from "react";

function FileUpload() {
  const [file, setFile] = useState(null);
  const [uploadStatus, setUploadStatus] = useState("");

  const handleFileChange = (event) => {
    setFile(event.target.files[0]); // ファイルを状態に保存
  };

  const handleFileUpload = async () => {
    if (!file) {
      setUploadStatus("ファイルを選択してください");
      return;
    }

    const formData = new FormData();
    formData.append("file", file); // フォームデータにファイルを追加

    try {
      const response = await fetch("https://your-server-endpoint.com/upload", {
        method: "POST",
        body: formData,
      });

      if (response.ok) {
        setUploadStatus("アップロード成功!");
      } else {
        setUploadStatus("アップロード失敗: サーバーエラー");
      }
    } catch (error) {
      console.error(error);
      setUploadStatus("アップロード失敗: ネットワークエラー");
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleFileUpload}>アップロード</button>
      <p>{uploadStatus}</p>
    </div>
  );
}

export default FileUpload;

コードのポイント

  1. FormDataの利用: ファイルデータを簡単に構成し、サーバーに送信するための標準的な方法です。
  2. 非同期処理: fetchasync/awaitでラップし、リクエスト結果を処理します。
  3. エラーハンドリング: ネットワークエラーやサーバーエラーを適切に捕捉して、ユーザーに通知します。

複数ファイルのアップロード


複数のファイルを一度にアップロードする場合も、FormDataを利用します。以下の例では、選択されたすべてのファイルを送信しています。

import React, { useState } from "react";

function MultiFileUpload() {
  const [files, setFiles] = useState([]);
  const [uploadStatus, setUploadStatus] = useState("");

  const handleFileChange = (event) => {
    setFiles(event.target.files); // ファイルリストを状態に保存
  };

  const handleFileUpload = async () => {
    if (files.length === 0) {
      setUploadStatus("ファイルを選択してください");
      return;
    }

    const formData = new FormData();
    Array.from(files).forEach((file, index) => {
      formData.append(`files[${index}]`, file); // 各ファイルを追加
    });

    try {
      const response = await fetch("https://your-server-endpoint.com/upload", {
        method: "POST",
        body: formData,
      });

      if (response.ok) {
        setUploadStatus("すべてのファイルがアップロードされました!");
      } else {
        setUploadStatus("アップロード失敗: サーバーエラー");
      }
    } catch (error) {
      console.error(error);
      setUploadStatus("アップロード失敗: ネットワークエラー");
    }
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileChange} />
      <button onClick={handleFileUpload}>アップロード</button>
      <p>{uploadStatus}</p>
    </div>
  );
}

export default MultiFileUpload;

進捗表示を追加する


アップロードの進捗状況を表示するには、XMLHttpRequestaxiosを利用します。以下はaxiosを使用した例です。

import React, { useState } from "react";
import axios from "axios";

function FileUploadWithProgress() {
  const [file, setFile] = useState(null);
  const [progress, setProgress] = useState(0);

  const handleFileChange = (event) => {
    setFile(event.target.files[0]);
  };

  const handleFileUpload = () => {
    if (!file) {
      alert("ファイルを選択してください");
      return;
    }

    const formData = new FormData();
    formData.append("file", file);

    axios.post("https://your-server-endpoint.com/upload", formData, {
      onUploadProgress: (progressEvent) => {
        const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
        setProgress(percentCompleted);
      },
    })
    .then(() => {
      alert("アップロード成功!");
    })
    .catch(() => {
      alert("アップロード失敗!");
    });
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleFileUpload}>アップロード</button>
      <p>アップロード進捗: {progress}%</p>
    </div>
  );
}

export default FileUploadWithProgress;

ポイント

  1. FormDataで柔軟なデータ構成: ファイルだけでなく、他のフィールドデータも送信可能。
  2. 進捗の追跡: 大きなファイルのアップロード時に、進捗状況を可視化することでUXを向上。
  3. 複数ファイルの効率的な処理: 配列やループを活用して柔軟に対応可能。

ファイルアップロード処理を実装することで、サーバーとの連携が強化され、実用的なアプリケーションを構築できます。次はエラーハンドリングについて詳しく見ていきます。

エラーハンドリング

Reactアプリケーションでファイルアップロードを実装する際、エラーハンドリングは非常に重要です。エラーが適切に処理されないと、ユーザー体験が大幅に損なわれる可能性があります。本セクションでは、アップロード処理中に発生する可能性のあるエラーの種類と、それらを処理する方法について解説します。

エラーの種類

  1. ユーザーエラー
  • ファイルが選択されていない。
  • 不適切なファイル形式やサイズが選択された。
  1. ネットワークエラー
  • サーバーが応答しない。
  • 接続タイムアウトやDNSエラー。
  1. サーバーエラー
  • サーバーが受信したデータを処理できない。
  • サーバー側のバリデーションエラー。
  1. プログラムエラー
  • バグや不適切なロジックによる処理失敗。

エラーハンドリングの基本実装

以下は、アップロード処理に対する一般的なエラーハンドリングの例です。

import React, { useState } from "react";

function FileUploadWithErrorHandling() {
  const [file, setFile] = useState(null);
  const [statusMessage, setStatusMessage] = useState("");

  const handleFileChange = (event) => {
    const selectedFile = event.target.files[0];
    if (!selectedFile) {
      setStatusMessage("ファイルが選択されていません。");
      return;
    }
    if (selectedFile.size > 5 * 1024 * 1024) { // 5MBを超えるファイルを拒否
      setStatusMessage("ファイルサイズが大きすぎます。5MB以下にしてください。");
      return;
    }
    setFile(selectedFile);
    setStatusMessage("");
  };

  const handleFileUpload = async () => {
    if (!file) {
      setStatusMessage("ファイルを選択してください。");
      return;
    }

    const formData = new FormData();
    formData.append("file", file);

    try {
      const response = await fetch("https://your-server-endpoint.com/upload", {
        method: "POST",
        body: formData,
      });

      if (!response.ok) {
        throw new Error(`サーバーエラー: ${response.status}`);
      }

      setStatusMessage("アップロード成功!");
    } catch (error) {
      console.error(error);
      setStatusMessage("アップロードに失敗しました。ネットワークやサーバーを確認してください。");
    }
  };

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={handleFileUpload}>アップロード</button>
      <p>{statusMessage}</p>
    </div>
  );
}

export default FileUploadWithErrorHandling;

エラーの種類ごとの対処法

1. ユーザーエラーの対処


入力内容を事前にバリデーションし、不適切なファイル形式やサイズを選択させないようにします。

const handleFileChange = (event) => {
  const file = event.target.files[0];
  if (file && !file.type.startsWith("image/")) {
    setStatusMessage("画像ファイルを選択してください。");
    return;
  }
};

2. ネットワークエラーの対処


ネットワークエラーはtry-catchブロック内でキャッチし、ユーザーに再試行を促します。

catch (error) {
  if (error.message.includes("Failed to fetch")) {
    setStatusMessage("ネットワーク接続に問題があります。もう一度試してください。");
  }
}

3. サーバーエラーの対処


サーバー側から返されたエラーコードやメッセージをユーザーに表示します。

if (response.status === 400) {
  setStatusMessage("サーバーエラー: 不適切なファイル形式です。");
}

4. プログラムエラーの対処


エラーが起きやすい箇所をテストし、予防的な処理を加えることで回避します。

エラー通知のユーザー体験向上

エラー通知は、わかりやすいメッセージで表示し、次に何をすべきかを明確にします。また、UIの工夫でユーザー体験を向上させることも重要です。

<p style={{ color: "red" }}>{statusMessage}</p>

ポイント

  1. 事前バリデーション: 不適切な入力をサーバーに送信する前に検出する。
  2. エラーメッセージの明確化: ユーザーが次に行うべき操作を示す。
  3. ロギング: 開発時や運用時のデバッグのため、詳細なエラー情報をログに残す。

適切なエラーハンドリングを実装することで、信頼性の高いアップロード機能を提供でき、ユーザーの満足度も向上します。次は複数ファイルアップロードの実装について解説します。

複数ファイルアップロードの実装

複数ファイルを一度にアップロードする機能は、多くのReactアプリケーションで必要とされます。Reactでは、<input type="file" multiple />を活用することで、複数のファイルを選択し、それらを効率的にアップロードできます。

基本的な複数ファイルアップロードの実装

以下は、複数ファイルを選択し、サーバーに送信する基本的な例です。

import React, { useState } from "react";

function MultiFileUpload() {
  const [files, setFiles] = useState([]);
  const [uploadStatus, setUploadStatus] = useState("");

  const handleFileChange = (event) => {
    const selectedFiles = Array.from(event.target.files);
    setFiles(selectedFiles);
  };

  const handleFileUpload = async () => {
    if (files.length === 0) {
      setUploadStatus("ファイルを選択してください。");
      return;
    }

    const formData = new FormData();
    files.forEach((file, index) => {
      formData.append(`files[${index}]`, file); // フォームデータに各ファイルを追加
    });

    try {
      const response = await fetch("https://your-server-endpoint.com/upload", {
        method: "POST",
        body: formData,
      });

      if (response.ok) {
        setUploadStatus("すべてのファイルが正常にアップロードされました!");
      } else {
        setUploadStatus("アップロードに失敗しました。");
      }
    } catch (error) {
      console.error(error);
      setUploadStatus("ネットワークエラー: アップロードに失敗しました。");
    }
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileChange} />
      <button onClick={handleFileUpload}>アップロード</button>
      <p>{uploadStatus}</p>
      <ul>
        {files.map((file, index) => (
          <li key={index}>{file.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default MultiFileUpload;

コードのポイント

  1. multiple属性: 複数ファイルを選択可能にする。
  2. Array.fromの利用: event.target.filesFileListオブジェクトであるため、配列操作を行うにはArray.fromで変換する。
  3. FormDataの活用: 複数のファイルを効率的に送信するためのデータ構造。

ファイルプレビュー付きの複数アップロード

選択した複数のファイルをリスト表示し、プレビューを加えることで、ユーザーの操作性を向上させます。

import React, { useState } from "react";

function MultiFileUploadWithPreview() {
  const [files, setFiles] = useState([]);

  const handleFileChange = (event) => {
    const selectedFiles = Array.from(event.target.files);
    setFiles(selectedFiles.map(file => ({
      file,
      preview: file.type.startsWith("image/") ? URL.createObjectURL(file) : null,
    })));
  };

  const handleFileUpload = async () => {
    if (files.length === 0) {
      alert("ファイルを選択してください。");
      return;
    }

    const formData = new FormData();
    files.forEach(({ file }, index) => {
      formData.append(`files[${index}]`, file);
    });

    try {
      const response = await fetch("https://your-server-endpoint.com/upload", {
        method: "POST",
        body: formData,
      });

      if (response.ok) {
        alert("アップロード成功!");
        setFiles([]);
      } else {
        alert("アップロードに失敗しました。");
      }
    } catch (error) {
      console.error(error);
      alert("ネットワークエラーが発生しました。");
    }
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileChange} />
      <button onClick={handleFileUpload}>アップロード</button>
      <div style={{ display: "flex", gap: "10px", flexWrap: "wrap" }}>
        {files.map(({ file, preview }, index) => (
          <div key={index}>
            {preview ? (
              <img
                src={preview}
                alt={`Preview ${index}`}
                style={{ maxWidth: "100px", maxHeight: "100px" }}
              />
            ) : (
              <p>{file.name}</p>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}

export default MultiFileUploadWithPreview;

コードの改善ポイント

  1. 画像プレビュー: URL.createObjectURLを使用して画像ファイルのプレビューを提供。
  2. リソース管理: 使用後はURL.revokeObjectURLでリソースを解放(省略可能だが推奨)。
  3. アップロード後の状態リセット: アップロード成功時に選択されたファイルリストをクリア。

サーバー側の設定例

複数ファイルを扱うには、サーバー側でも適切に処理する必要があります。以下は、Node.jsとmulterを使用した例です。

const express = require('express');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

const app = express();

app.post('/upload', upload.array('files', 10), (req, res) => {
  console.log(req.files); // アップロードされたファイル情報
  res.status(200).send("ファイルアップロード成功");
});

app.listen(3000, () => {
  console.log('サーバーがポート3000で起動しました');
});

ポイント

  1. 効率的なデータ処理: ファイルリストをループしてアップロード。
  2. ユーザー体験の向上: プレビューを提供し、アップロード内容を確認可能にする。
  3. サーバーとの連携: フロントエンドとバックエンドをシームレスに接続。

これにより、複数ファイルのアップロード機能が直感的かつ効率的に実現されます。次は、アップロード後のデータ管理について解説します。

アップロードされたデータの管理

ファイルがサーバーにアップロードされた後、アップロードされたデータを適切に管理することが重要です。これには、データのストレージ、メタデータの追跡、状態管理、さらにはユーザーへのフィードバックが含まれます。本セクションでは、アップロード後のデータ管理のベストプラクティスについて解説します。

アップロード後のデータ確認

サーバーからアップロード結果を返すことで、フロントエンドでアップロードが成功したファイルを確認・表示することができます。

import React, { useState } from "react";

function FileUploadManagement() {
  const [files, setFiles] = useState([]);
  const [uploadedFiles, setUploadedFiles] = useState([]);
  const [statusMessage, setStatusMessage] = useState("");

  const handleFileChange = (event) => {
    setFiles(Array.from(event.target.files));
  };

  const handleFileUpload = async () => {
    if (files.length === 0) {
      setStatusMessage("ファイルを選択してください。");
      return;
    }

    const formData = new FormData();
    files.forEach((file) => {
      formData.append("files", file);
    });

    try {
      const response = await fetch("https://your-server-endpoint.com/upload", {
        method: "POST",
        body: formData,
      });

      if (response.ok) {
        const result = await response.json(); // アップロードされたファイルの情報を取得
        setUploadedFiles(result.uploadedFiles); // サーバーから返された情報を状態に保存
        setStatusMessage("アップロード成功!");
      } else {
        setStatusMessage("アップロード失敗: サーバーエラー");
      }
    } catch (error) {
      console.error(error);
      setStatusMessage("ネットワークエラー: アップロードに失敗しました。");
    }
  };

  return (
    <div>
      <input type="file" multiple onChange={handleFileChange} />
      <button onClick={handleFileUpload}>アップロード</button>
      <p>{statusMessage}</p>
      <h3>アップロード済みファイル</h3>
      <ul>
        {uploadedFiles.map((file, index) => (
          <li key={index}>{file.name}</li>
        ))}
      </ul>
    </div>
  );
}

export default FileUploadManagement;

状態管理のポイント

  1. アップロードファイルの追跡
  • サーバーから返されるファイルのメタデータ(ファイル名、URL、サイズなど)を状態として管理し、後で使用する。
  1. アップロード状況の可視化
  • 成功したファイルと失敗したファイルを区別してリストに表示する。
  1. サーバー側のレスポンス例
{
  "uploadedFiles": [
    { "name": "example1.jpg", "url": "/uploads/example1.jpg", "size": 1024 },
    { "name": "example2.png", "url": "/uploads/example2.png", "size": 2048 }
  ]
}

アップロードファイルのストレージと表示

アップロードされたファイルをストレージに保存し、フロントエンドでプレビューやダウンロードリンクとして利用することができます。

import React from "react";

function UploadedFileList({ files }) {
  return (
    <div>
      {files.length > 0 && <h3>アップロード済みのファイル</h3>}
      <ul>
        {files.map((file, index) => (
          <li key={index}>
            <a href={file.url} target="_blank" rel="noopener noreferrer">
              {file.name}
            </a> ({(file.size / 1024).toFixed(2)} KB)
          </li>
        ))}
      </ul>
    </div>
  );
}

export default UploadedFileList;

メタデータの保存

アップロードされたファイルのメタデータ(例: 名前、URL、アップロード日時など)をデータベースに保存することで、将来的な参照や管理が容易になります。

サーバー側のコード例(Node.js + MongoDB):

const mongoose = require("mongoose");

const FileSchema = new mongoose.Schema({
  name: String,
  url: String,
  size: Number,
  uploadedAt: { type: Date, default: Date.now },
});

const File = mongoose.model("File", FileSchema);

// ファイル保存処理
app.post("/upload", upload.array("files"), async (req, res) => {
  const savedFiles = await Promise.all(
    req.files.map((file) => {
      const newFile = new File({
        name: file.originalname,
        url: `/uploads/${file.filename}`,
        size: file.size,
      });
      return newFile.save();
    })
  );

  res.json({ uploadedFiles: savedFiles });
});

ポイント

  1. ユーザー向けの情報提供
  • アップロード後、ユーザーにファイルのURLや関連情報を提供。
  1. データの永続化
  • サーバー側でファイルのメタデータを管理し、アプリケーション全体で一貫性を確保。
  1. 柔軟な管理
  • アップロードされたファイルを削除、更新できるインターフェースを提供。

適切なアップロード後のデータ管理により、ユーザー体験を向上させるだけでなく、運用上の効率も大幅に改善されます。次はこの記事全体のまとめです。

まとめ

本記事では、Reactを用いたファイル入力(onChange)イベントの処理から、アップロード、プレビュー表示、複数ファイルの処理、エラーハンドリング、そしてアップロード後のデータ管理までを詳しく解説しました。

Reactでファイル処理を実装する際には、onChangeイベントを基盤とし、FormDataや状態管理を組み合わせることで柔軟な機能を構築できます。さらに、ユーザー体験を向上させるためには、エラーハンドリングやプレビュー表示などの工夫が重要です。

これらの知識を活用することで、ファイルアップロードを伴うさまざまなアプリケーションに対応可能な、信頼性の高い機能を開発できるようになります。次のステップとして、この記事の内容を実際のプロジェクトに応用してみてください。

コメント

コメントする

目次
  1. ファイル入力(onChange)イベントとは
    1. onChangeイベントの仕組み
    2. 基本的な使用例
    3. onChangeイベントのポイント
  2. ファイル入力フォームの作成
    1. 基本的なファイル入力フォーム
    2. ファイル入力フォームの状態管理
    3. カスタマイズされたファイル入力フォーム
    4. ポイント
  3. ファイルデータの取得方法
    1. イベントオブジェクトを使用したファイル情報の取得
    2. 取得できるファイル情報
    3. 複数ファイルの処理
    4. ファイルデータの状態管理
    5. ポイント
  4. ファイルデータのプレビュー表示
    1. 画像ファイルのプレビュー表示
    2. テキストファイルのプレビュー表示
    3. 複数ファイルのプレビュー表示
    4. ポイント
  5. ファイルのアップロード処理
    1. 基本的なアップロード処理
    2. コードのポイント
    3. 複数ファイルのアップロード
    4. 進捗表示を追加する
    5. ポイント
  6. エラーハンドリング
    1. エラーの種類
    2. エラーハンドリングの基本実装
    3. エラーの種類ごとの対処法
    4. エラー通知のユーザー体験向上
    5. ポイント
  7. 複数ファイルアップロードの実装
    1. 基本的な複数ファイルアップロードの実装
    2. コードのポイント
    3. ファイルプレビュー付きの複数アップロード
    4. コードの改善ポイント
    5. サーバー側の設定例
    6. ポイント
  8. アップロードされたデータの管理
    1. アップロード後のデータ確認
    2. 状態管理のポイント
    3. アップロードファイルのストレージと表示
    4. メタデータの保存
    5. ポイント
  9. まとめ