Next.jsでローカルファイルを選択する

に公開

今回はNext.jsでファイルのファイルを選択選択し、選択されたファイル名を表示させる実装についての記事になります。

まずは今回の実装の要件をまとめておきたいと思います。
①画像ファイル(jpeg/jpg/png)を選択、登録できるようにしたい。
②ファイル選択後、選択されたファイル名が画面上に表示される。
③ファイル選択はHTML標準のinputタグではなく、自作のボタンを使用して行いたい。
です。
※ 今回はMUIを使用しています。

まずは今回の実装の全体になります。

const FileRegister = () => {
  const [file, setFile] = useState<File | null>(null);
  const filePickerRef = useRef<HTMLInputElement>(null);
  const showFolder = () => {
    if (filePickerRef.current) {
      filePickerRef.current.click();
    }
  };

  const selectFile = (e: ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (files && files.length !== 0) {
      setFile(files[0]);
    }
    e.target.value = '';
  };

  return (
    <Stack>
        <Grid>
          <input
            type="file"
            accept=".png, .jpeg, .jpg "
            ref={filePickerRef}
            onChange={(e) => selectFile(e)}
            hidden
          />
          <Button onClick={showFolder}>ファイル登録</Button>
          <Typography>{file?.name}</Typography>
        </Grid>
    </Stack>
  );
};

export default FileRegister;


では要件ごとにコードの方を解説していきたいと思います。

①画像ファイル(jpeg/jpg/png)を選択、登録できるようにしたい。

今回はinputタグ内の"accept"で".png, .jpeg, .jpg"を指定することで選択できるファイルを絞り込んでいます。

<input
  type="file"
  accept=".png, .jpeg, .jpg "
  ref={filePickerRef}
  onChange={(e) => selectFile(e)}
  hidden
/>


②ファイル選択後、選択されたファイル名が画面上に表示される。

こちらは選択されたファイルをstateで管理するようにします。

const [file, setFile] = useState<File | null>(null);


そしてstateが更新されれば、ファイル名を表示させるようにします。

<Typography>{file?.name}</Typography>


③ファイル選択はHTML標準のinputタグではなく、自作のボタンを使用して行いたい。

こちらはすこし工夫が必要です。

まずはinputタグを用意し

  • useRefでinputのDOMを保持するようにする
  • ファイル選択(onChange)でstate(ファイル情報)更新。具体的な"selectFile"の説明は後程。
  • inputタグ自体はhiddenで非表示にする
const filePickerRef = useRef<HTMLInputElement>(null);

--省略--

<input 
  type="file"
  accept=".png, .jpeg, .jpg "
  ref={filePickerRef}
  onChange={(e) => selectFile(e)}
  hidden
/>


hiddenで非表示になっているのでこのinput自体は直接操作することができません。
実際にファイル選択をする操作をするButtonを以下のように用意します。

const showFolder = () => {
    if (filePickerRef.current) {
      filePickerRef.current.click();
    }
  };

-- 省略 --

<Button type="button" onClick={showFolder}>ファイル登録</Button>


useRefのclick()を利用することで
"ファイル登録"ボタンをクリック→useRefに登録されているinputタグをクリックする挙動になります。

では最後にファイル選択の実装について見ていきます。

const selectFile = (e: ChangeEvent<HTMLInputElement>) => {
  const files = e.target.files;
  if (files && files.length !== 0) {
    setFile(files[0]);
  }
};

-- 省略 --

<input
  type="file"
  accept=".png, .jpeg, .jpg "
  ref={filePickerRef}
  onChange={(e) => selectFile(e)}
  hidden
/>


ファイル選択のダイアログで選択されたファイルは"selectFile"に渡ることになります。
selectFile内での処理ですが、e.target.filesには配列が入り、その配列の先頭に選択されたfile(files[0]に入ります)が来ることになります。
選択されたfileがあれば、そのfileでstateを更新します。

補足になりますが、もし選択したファイルを削除できるようなボタンを用意する場合は、

<button type="button" onClcik={() => setfile(null)}>ファイル削除</button>


でfileをnullに更新し、ファイル名の表示をクリアすることができますが、
inputに登録されたvalueが先ほど選択したファイル情報が入ったままになってしますので、
再度同じファイルを選択した場合、"onChange"が効かなくなります。(変化なしのため)

ですので最後にe.target.valueを初期化しておくことが必要になってきます。

const selectFile = (e: ChangeEvent<HTMLInputElement>) => {
  const files = e.target.files;
  if (files && files.length !== 0) {
    setFile(files[0]);
  }
  e.target.value = "";
};


以上、ファイル選択の実装についてでした。