참고
- 파일 브라우저 만들기 (React File Browser with chonky)
- glob으로 파일, 폴더 목록 찾기
- Mui Tree View로 파일, 폴더 뷰 만들기
- Mui Tree View로 파일, 폴더 뷰 만들기 (with Node JS)
- Mui로 파일 브라우저 만들기
- 파일 브라우저 만들기 (with Node JS)
- 파일 브라우저에서 파일 다운로드하기
파일, 폴더 뷰를 만들었으니, 다음과 같이 리액트 파일 브라우저(File Browser)를 만들어보자.
그리드를 이용하여 폴더 뷰 / 브라우저 분할하기
이전 글에서 만든 TreeViewExample2를 복사해서 FileBrowser로 바꾼다.
그리고 div의 style에 display를 grid로 설정하고 아래와 같이 적절히 화면을 분할한다.
const FileBrowser = () => {
const [treeInfo, setTreeInfo] = useState({});
...
return (
<div
style={{
display: "grid",
gridTemplateColumns: "20% 1% auto",
gridGap: "25px",
width: "100%",
}}
>
<div>
<TreeView
aria-label="file system navigator"
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
sx={{ height: 500, overflowX: "hidden" }}
>
{makeTreeItem(treeInfo, "")}
</TreeView>
</div>
<div style={{ borderRight: "2px solid black" }} />
<div>
<div>File Browser</div>
</div>
</div>
);
};
TreeView / 수직선 / File Browser를 포함하는 부모의 div에 20% 1% auto 비율로 화면을 분할하였다.
FileUI Component 만들기
FileBrowser에서는 아래와 같이 여러 파일들의 절대경로가 props로 넘겨지게 된다.
여기서는 임시로 경로를 추가해보자.
<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>
FileUI는 다음과 같이 구성된다.
파일이 아닌 경우( = 확장자가 없는 경우)는 폴더 아이콘을, 그리고 txt와 json은 아래와 같이 분리하였다.
아이콘 밑에는 폴더/파일 이름이 나타나게 된다.
주어지는 pathInfo에서 "/"로 split을 한 후, 마지막 값을 취하면 폴더/파일 이름을 알 수 있다.
const getFileName = (path) => {
if (path === undefined) return undefined;
let spt = path.split("/");
return spt[spt.length - 1];
};
그리고 위에서 얻은 fileName으로 Icon 모양을 결정하면 된다.
const getMuiIcon = (fileName) => {
if (fileName === undefined) return <TextSnippetIcon sx={{ fontSize: 60 }} />;
let spt = fileName.split(".");
if (spt.length === 1)
return <FolderIcon sx={{ fontSize: 60, left: "50%" }} />;
if (spt[1] === "json")
return <DataObjectICON sx={{ fontSize: 60, left: "50%" }} />;
return <TextSnippetIcon sx={{ fontSize: 60, left: "50%" }} />;
};
FileUI는 다음과 같다.
const FileUI = ({ pathInfo }) => {
return (
<div
style={{
display: "inline-block",
width: "110px",
height: "120px",
backgroundColor: "silver",
textAlign: "center",
margin: "10px",
cursor: "pointer",
}}
onClick={(e) => console.log(e)}
>
{getMuiIcon(getFileName(pathInfo))}
<div style={{ width: "110px", height: "40px", wordBreak: "break-all" }}>
<span
style={{
fontSize: "12px",
backgroundColor: "green",
cursor: "pointer",
display: "block",
height: "50px",
}}
>
{getFileName(pathInfo)}
</span>
</div>
</div>
);
};
위와 같이 css를 작성하면 파일 이름이 길어지는 경우 아래처럼 FileUI의 모양이 어긋나게 된다.
span에 입력된 text의 아래 기준으로 정렬이 되기 때문이다.
상위의 div에 verticalAlign을 top으로 맞춰주면 해결할 수 있다.
<div
style={{
display: "inline-block",
width: "110px",
height: "120px",
backgroundColor: "silver",
textAlign: "center",
margin: "10px",
cursor: "pointer",
verticalAlign: "top",
}}
onClick={(e) => console.log(e)}
>
다음 글에서 Node JS로 경로를 가져온 후, 파일 브라우저에 연동해보자.
전체 코드는 다음과 같다.
FileBrowser.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";
let localData = {
child: [
{
label: "D:",
nodeId: 0,
child: [
{
label: "github",
nodeId: 1,
child: [
{
label: "globfiles",
nodeId: 2,
child: [
{
label: "abc1",
nodeId: 3,
child: [
{ label: "abc1_jsonfile1.json", nodeId: 4 },
{ label: "abc1_jsonfile2.json", nodeId: 5 },
{ label: "abc1_textfile1.txt", nodeId: 6 },
{ label: "abc1_textfile2.txt", nodeId: 7 },
{
label: "abc2",
nodeId: 8,
child: [
{ label: "abc2_jsonfile.json", nodeId: 12 },
{
label: "abc3",
nodeId: 13,
child: [
{ label: "abc3_jsonfile.json", nodeId: 14 },
{ label: "abc3_textfile.txt", nodeId: 15 },
],
},
],
},
{
label: "abc2_2",
nodeId: 9,
child: [
{ label: "abc2_2_jsonfile.json", nodeId: 10 },
{ label: "abc2_2_textfile.txt", nodeId: 11 },
],
},
],
},
{
label: "def1",
nodeId: 16,
child: [
{ label: "def_jsonfile1.json", nodeId: 17 },
{ label: "def_jsonfile2.json", nodeId: 18 },
{ label: "def_textfile1.txt", nodeId: 19 },
{ label: "def_textfile2.txt", nodeId: 20 },
],
},
{
label: "ghi1",
nodeId: 21,
child: [
{
label: "ghi2",
nodeId: 22,
child: [
{ label: "ghi2_jsonfile1.json", nodeId: 23 },
{ label: "ghi2_jsonfile2.json", nodeId: 24 },
{ label: "ghi2_textfile1.txt", nodeId: 25 },
{ label: "ghi2_textfile2.txt", nodeId: 26 },
],
},
],
},
{ label: "jsonfile1.json", nodeId: 27 },
{ label: "jsonfile2.json", nodeId: 28 },
{ label: "textfile1.txt", nodeId: 29 },
{ label: "textfile2.txt", nodeId: 30 },
],
},
],
},
],
},
],
};
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%",
}}
>
<div>
<TreeView
aria-label="file system navigator"
defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />}
sx={{ height: 500, overflowX: "hidden" }}
>
{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;
FileUI.js
import React from "react";
import TextSnippetIcon from "@mui/icons-material/TextSnippet";
import DataObjectICON from "@mui/icons-material/DataObject";
import FolderIcon from "@mui/icons-material/Folder";
const getFileName = (path) => {
if (path === undefined) return undefined;
let spt = path.split("/");
return spt[spt.length - 1];
};
const getMuiIcon = (fileName) => {
if (fileName === undefined) return <TextSnippetIcon sx={{ fontSize: 60 }} />;
let spt = fileName.split(".");
if (spt.length === 1)
return <FolderIcon sx={{ fontSize: 60, left: "50%" }} />;
if (spt[1] === "json")
return <DataObjectICON sx={{ fontSize: 60, left: "50%" }} />;
return <TextSnippetIcon sx={{ fontSize: 60, left: "50%" }} />;
};
const FileUI = ({ pathInfo }) => {
return (
<div
style={{
display: "inline-block",
width: "110px",
height: "120px",
backgroundColor: "silver",
textAlign: "center",
margin: "10px",
cursor: "pointer",
verticalAlign: "top",
}}
onClick={(e) => console.log(e)}
>
{getMuiIcon(getFileName(pathInfo))}
<div style={{ width: "110px", height: "40px", wordBreak: "break-all" }}>
<span
style={{
fontSize: "12px",
backgroundColor: "green",
cursor: "pointer",
display: "block",
height: "50px",
}}
>
{getFileName(pathInfo)}
</span>
</div>
</div>
);
};
export default FileUI;
댓글