본문 바로가기
개발/React

React Material - 경로를 가져와서 파일 브라우저 만들기 (React File Browser with Mui + Node JS)

by 피로물든딸기 2023. 6. 2.
반응형

리액트 전체 링크

Node JS 전체 링크

 

참고

- 파일 브라우저 만들기 (React File Browser with chonky)

 

- glob으로 파일, 폴더 목록 찾기
Mui Tree View로 파일, 폴더 뷰 만들기
Mui Tree View로 파일, 폴더 뷰 만들기 (with Node JS)
Mui로 파일 브라우저 만들기 
파일 브라우저 만들기 (with Node JS)
파일 브라우저에서 파일 다운로드하기

 

파일 브라우저를 로컬에서 만들었으니, Node JS 서버와 파일 브라우저를 연동해보자.

 

위의 폴더/파일이 React Mui로 File Browser가 나오도록 구현하였다.

 


Node JS 연동

 

먼저 FileBrowser에 localData를 지우고, 다시 Node JS와 연동한다.

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

import TreeView from "@mui/lab/TreeView";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import TreeItem from "@mui/lab/TreeItem";
import FileUI from "./FileUI";

const FileBrowser = () => {
  const [treeInfo, setTreeInfo] = useState({});

  let tmpTreeInfo = {};
  let nodeId = 0;
  const appendChild = (arr, info) => {
    if (arr.child === undefined) arr.child = [];
    if (arr.child.findIndex((item) => item.label === info) === -1) {
      arr.child.push({ label: info, nodeId });
      nodeId++;
    }
  };

  const makeDirectories = (directories) => {
    tmpTreeInfo = {};
    for (let d of directories) {
      let split = d.split("/");
      let len = split.length;
      let current = tmpTreeInfo;

      for (let i = 0; i < len; i++) {
        appendChild(current, split[i]);
        current = current.child.find((item) => item.label === split[i]);
      }
    }

    console.log(tmpTreeInfo);
    setTreeInfo(tmpTreeInfo);
  };
  
  const getFiles = () => {
    // setTreeInfo(localData);
    // return;
    let server = `http://192.168.55.120:3002`;
    let path = `D:\\github\\globfiles\\**`;
    fetch(`${server}/useGlob?path=${path}`)
      .then((res) => res.json())
      .then((data) => makeDirectories(data.findPath));
  };

  const makeTreeItem = (info, parent) => {
    if (info.child === undefined) return;

    return info.child.map((item, idx) => (
      <TreeItem
        key={idx}
        nodeId={item.nodeId.toString()}
        label={item.label}
        onClick={() => console.log(`${parent}/${item.label}`)}
      >
        {makeTreeItem(item, `${parent}/${item.label}`)}
      </TreeItem>
    ));
  };

  useEffect(() => {
    getFiles();
  }, []);

  return (
    <div
      style={{
        display: "grid",
        gridTemplateColumns: "20% 1% auto",
        gridGap: "25px",
        width: "100%",
      }}
    >
      {/* <button onClick={getFiles}>test</button> */}
      <div>
        <TreeView
          aria-label="file system navigator"
          defaultCollapseIcon={<ExpandMoreIcon />}
          defaultExpandIcon={<ChevronRightIcon />}
          sx={{ height: 500, overflowX: "hidden" }}
          //sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
        >
          {makeTreeItem(treeInfo, "")}
        </TreeView>
      </div>
      <div style={{ borderRight: "2px solid black" }} />
      <div>
        <FileUI pathInfo={"test/folder/temp"}/>
        <FileUI pathInfo={"test/folder/abcdef.json"}/>
        <FileUI pathInfo={"test/folder/abc12341234.txt"}/>
        <FileUI pathInfo={"test/folder/abc12341234.txt"}/>
        <FileUI pathInfo={"test/folder/abc1234123412341234.txt"}/>
        <FileUI pathInfo={"test/folder/abc123412341234123412341234.json"}/>
        <FileUI pathInfo={"test/folder/text.csv"}/>
        <FileUI pathInfo={"test/folder/text.txt"}/>
        <FileUI pathInfo={"test/folder/text.txt"}/>
        <FileUI pathInfo={"test/folder/text.txt"}/>     
      </div>
    </div>
  );
};

export default FileBrowser;

 

현재 구현된 내용으로 TreeItem에는 onClick에서 해당 경로를 출력하도록 구현하였다.

  const makeTreeItem = (info, parent) => {
    if (info.child === undefined) return;

    return info.child.map((item, idx) => (
      <TreeItem
        key={idx}
        nodeId={item.nodeId.toString()}
        label={item.label}
        onClick={() => console.log(`${parent}/${item.label}`)}
      >
        {makeTreeItem(item, `${parent}/${item.label}`)}
      </TreeItem>
    ));
  };

 

실제로 폴더 뷰를 클릭하면 경로가 출력된다.


선택한 폴더 정보 가져오기

 

getFiles를 참고하여 아래의 함수를 만든다.

const [fileUi, setFileUI] = useState([]);

...

  const getFilesForFileBrowser = (path) => {
    let server = `http://192.168.55.120:3002`;

    /* /D:/... 앞의 / 삭제 */
    path = path.substring(1, path.length); 

    fetch(`${server}/useGlob?path=${path}/*`)
      .then((res) => res.json())
      .then((data) => setFileUI(data.findPath));
  }

 

makeTreeItem의 onClick을 위의 메서드로 수정한다.

그러면 fileUi에 useState로 현재 선택된 폴더에 대한 정보를 가져올 수 있다.

  const makeTreeItem = (info, parent) => {
    if (info.child === undefined) return;

    return info.child.map((item, idx) => (
      <TreeItem
        key={idx}
        nodeId={item.nodeId.toString()}
        label={item.label}
        onClick={() => getFilesForFileBrowser(`${parent}/${item.label}`)}
      >
        {makeTreeItem(item, `${parent}/${item.label}`)}
      </TreeItem>
    ));
  };

 

TreeView에 있던 FileUI는 모두 삭제하고 fileUi를 map을 이용해 표현한다.

  <div>
    <TreeView
      aria-label="file system navigator"
      defaultCollapseIcon={<ExpandMoreIcon />}
      defaultExpandIcon={<ChevronRightIcon />}
      sx={{ height: 500, overflowX: "hidden" }}
      //sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
    >
      {makeTreeItem(treeInfo, "")}
    </TreeView>
  </div>
  <div style={{ borderRight: "2px solid black" }} />
  <div>
      {fileUi.map((f) => <FileUI pathInfo={f}/>)}
  </div>

 

다음과 같은 에러가 난다면 key를 추가하면 된다.

{fileUi.map((f, idx) => <FileUI key={idx} pathInfo={f}/>)}

 

이제 React를 실행하면 선택한 폴더에 있는 폴더와 파일이 보이게 된다.

 

전체 코드는 다음과 같다.

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

import TreeView from "@mui/lab/TreeView";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ChevronRightIcon from "@mui/icons-material/ChevronRight";
import TreeItem from "@mui/lab/TreeItem";
import FileUI from "./FileUI";

const FileBrowser = () => {
  const [treeInfo, setTreeInfo] = useState({});
  const [fileUi, setFileUI] = useState([]);

  let tmpTreeInfo = {};
  let nodeId = 0;
  const appendChild = (arr, info) => {
    if (arr.child === undefined) arr.child = [];
    if (arr.child.findIndex((item) => item.label === info) === -1) {
      arr.child.push({ label: info, nodeId });
      nodeId++;
    }
  };

  const makeDirectories = (directories) => {
    tmpTreeInfo = {};
    for (let d of directories) {
      let split = d.split("/");
      let len = split.length;
      let current = tmpTreeInfo;

      for (let i = 0; i < len; i++) {
        appendChild(current, split[i]);
        current = current.child.find((item) => item.label === split[i]);
      }
    }

    console.log(tmpTreeInfo);
    setTreeInfo(tmpTreeInfo);
  };
  
  const getFiles = () => {
    // setTreeInfo(localData);
    // return;
    let server = `http://192.168.55.120:3002`;
    let path = `D:\\github\\globfiles\\**`;

    fetch(`${server}/useGlob?path=${path}`)
      .then((res) => res.json())
      .then((data) => makeDirectories(data.findPath));
  };

  const getFilesForFileBrowser = (path) => {
    let server = `http://192.168.55.120:3002`;

    /* /D:/... 앞의 / 삭제 */
    path = path.substring(1, path.length); 

    fetch(`${server}/useGlob?path=${path}/*`)
      .then((res) => res.json())
      .then((data) => setFileUI(data.findPath));
  }

  const makeTreeItem = (info, parent) => {
    if (info.child === undefined) return;

    return info.child.map((item, idx) => (
      <TreeItem
        key={idx}
        nodeId={item.nodeId.toString()}
        label={item.label}
        onClick={() => getFilesForFileBrowser(`${parent}/${item.label}`)}
      >
        {makeTreeItem(item, `${parent}/${item.label}`)}
      </TreeItem>
    ));
  };

  useEffect(() => {
    getFiles();
  }, []);

  return (
    <div
      style={{
        display: "grid",
        gridTemplateColumns: "20% 1% auto",
        gridGap: "25px",
        width: "100%",
      }}
    >
      {/* <button onClick={getFiles}>test</button> */}
      <div>
        <TreeView
          aria-label="file system navigator"
          defaultCollapseIcon={<ExpandMoreIcon />}
          defaultExpandIcon={<ChevronRightIcon />}
          sx={{ height: 500, overflowX: "hidden" }}
          //sx={{ height: 240, flexGrow: 1, maxWidth: 400, overflowY: "auto" }}
        >
          {makeTreeItem(treeInfo, "")}
        </TreeView>
      </div>
      <div style={{ borderRight: "2px solid black" }} />
      <div>
         {fileUi.map((f, idx) => <FileUI key={idx} pathInfo={f}/>)}
      </div>
    </div>
  );
};

export default FileBrowser;
반응형

댓글