참고
- glob으로 파일, 폴더 목록 찾기
- Mui Tree View로 파일, 폴더 뷰 만들기
- Mui Tree View로 파일, 폴더 뷰 만들기 (with Node JS)
- Mui로 파일 브라우저 만들기
- 파일 브라우저 만들기 (with Node JS)
- 파일 브라우저에서 파일 다운로드하기
리액트에서 노드 서버를 통해 파일을 다운로드 해보자.
여기서는 파일 브라우저에서 파일을 클릭하면 클릭한 파일이 다운로드 되도록 한다.
Node JS에서 stream으로 파일 다운로드 구현하기
먼저 node에서 mime을 설치한다.
$ npm install mime
Node에서 downloadFile.js를 다음과 같이 구현한다.
mime과 fs의 stream을 이용하였다.
const express = require("express");
const router = express.Router();
const fs = require("fs");
const mime = require("mime");
router.get("/", (req, res) => {
let spt = req.query.filePath.split("/");
let fileName = spt[spt.length - 1]; // 파일명만 추출
let mimetype = mime.getType(req.query.filePath); // 파일의 타입을 가져온다.
console.log(mimetype);
res.setHeader(
"Content-disposition",
`attachment; fileName ${encodeURI(fileName)}` // 다운 받을 파일 이름 설정
);
res.setHeader("Content-type", mimetype); // 파일의 형식 지정
let fileStream = fs.createReadStream(req.query.filePath);
fileStream.pipe(res);
});
module.exports = router;
mimetype을 나중에 출력해보자.
react에서 클릭한 파일이 json이라면 mimetype = application/json이 되고, txt는 text/plain이라고 출력된다.
application/json
text/plain
이 형식을 mime으로 type을 알 수 있기 때문에 setHeader에 설정하기만 하면 된다.
server.js에 downloadFile.js의 라우터를 추가한다.
const express = require("express");
const app = express();
const cors = require("cors");
app.use(cors());
const useGlob = require("./routes/useGlob");
const downloadFile = require("./routes/downloadFile");
app.use("/useGlob", useGlob);
app.use("/downloadFile", downloadFile);
app.listen(3002, () =>
console.log("Node.js Server is running on port 3002...")
);
React에서 파일 다운로드 구현하기
FileUI에서 onClick을 발생하면 다운로드가 되도록 수정하자.
const FileUI = ({ pathInfo }) => {
return (
<div
style={{
display: "inline-block",
width: "110px",
height: "120px",
backgroundColor: "silver",
textAlign: "center",
margin: "10px",
cursor: "pointer",
verticalAlign: "top",
}}
onClick={() => download(pathInfo)}
>
파일을 다운로드하기 위해 axios를 설치한다.
$ npm install axios
download 함수는 다음을 참고하자. (자바스크립트 파일 다운로드 참고)
폴더인 경우는 다운로드 할 필요가 없기 때문에 getFileName으로 얻은 값에 "."이 없다면 return 하였다.
axios에서 node server로 downloadFile과 filePath query를 넘겨준다.
이때 받은 응답(response)으로 file을 다운로드 하도록 구현하였다.
const downloadPC = (response, fileName) => {
const url = window.URL.createObjectURL(
new Blob([response.data], { type: `${response.headers["content-type"]}` })
);
let link = document.createElement("a");
link.href = url;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url); // memory 해제
};
const download = (pathInfo) => {
if (pathInfo === undefined || pathInfo === "") return;
let fileName = getFileName(pathInfo);
if (fileName.includes(".") === false) return; // 폴더인 경우
let server = `http://192.168.55.120:3002`;
axios
.get(`${server}/downloadFile?filePath=${pathInfo}`, {
responseType: "arraybuffer",
})
.then((res) => {
downloadPC(res, fileName);
})
.catch((error) => console.log(error));
};
전체 코드는 다음과 같다.
FileUI.js
import React from "react";
import axios from "axios";
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 downloadPC = (response, fileName) => {
const url = window.URL.createObjectURL(
new Blob([response.data], { type: `${response.headers["content-type"]}` })
);
let link = document.createElement("a");
link.href = url;
link.setAttribute("download", fileName);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url); // memory 해제
};
const download = (pathInfo) => {
if (pathInfo === undefined || pathInfo === "") return;
let fileName = getFileName(pathInfo);
if (fileName.includes(".") === false) return; // 폴더인 경우
let server = `http://192.168.55.120:3002`;
axios
.get(`${server}/downloadFile?filePath=${pathInfo}`, {
responseType: "arraybuffer",
})
.then((res) => {
downloadPC(res, fileName);
})
.catch((error) => console.log(error));
};
const FileUI = ({ pathInfo }) => {
return (
<div
style={{
display: "inline-block",
width: "110px",
height: "120px",
backgroundColor: "silver",
textAlign: "center",
margin: "10px",
cursor: "pointer",
verticalAlign: "top",
}}
onClick={() => download(pathInfo)}
>
{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;
댓글