본문 바로가기
개발/React

React Material - 파일 브라우저에서 파일 다운로드하기 (React File Download with Node JS)

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

리액트 전체 링크

Node JS 전체 링크

 

참고

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;
반응형

댓글