본문 바로가기
개발/Node JS

Node js, React 파일 관리 시스템 만들기 (10)

by 피로물든딸기 2021. 7. 16.
반응형

프로젝트 전체 링크

 

이전 - (9) csv 파일 목록 불러오기

현재 - (10) csv 파일 handsontable로 연동하기

다음 - (11) AutoSizeInput에 파일 이름 연동하기

 

깃허브에서 코드 확인하기


이제 file까지 불러오게 되었으니, 실제 csv를 handsontable로 보여주자.

 

node js에 getFile.js를 추가한다.

readFileSync를 이용하여 utf-8로 file을 읽어서 send한다.

// getFile.js
const express = require("express");
const fs = require("fs");
const router = express.Router();

router.get("/", (req, res) => {
  let path = req.query.path;
  let info = fs.readFileSync(`${path}`, "utf8");

  res.send({ info });
});

module.exports = router;

 

getFile.js를 추가하였으니 server.js에도 추가한다.

//server.js
const express = require('express');
const app = express();
const nodetest = require('./routes/nodetest');
const getFileFolderList = require('./routes/getFileFolderList');
const getFile = require('./routes/getFile');

const cors = require('cors');
app.use(cors()); //npm install cors --save

app.use('/nodetest', nodetest);
app.use('/getFileFolderList', getFileFolderList);
app.use('/getFile', getFile);

app.listen(3002, () => console.log('Node.js Server is running on port 3002...'));

 

이제 react의 nodelibrary.js에 getFile 함수를 만든다.

//nodelibrary.js

...

export const getFile = (path) => {
    fetch(`${MY_SERVER}/getFile?path=${path}`)
    .then((response) => response.json())
    .then((data) => console.log(data));
}

 

 

App.js에 아래의 useEffect 코드를 추가하고, option에서 file을 고르면 파일 내용을 로그로 볼 수 있다.

useEffect(() => mnode.getFile(`${mnode.PATH}/${version}/${country}/${file}`), [file]);

 

참고로 모든 csv 파일은 아래와 같은 내용이 담겨있다.


이제 App.js의 csv 파일의 path 정보가 되는 version, country, file을 FileUpload에 넘겨주자.

handsontable에서 upload 코드를 재활용하면 server의 csv를 바로 불러올 수 있다.

아래와 같이 pathInfo에 version, country, file을 한꺼번에 담자.

<FileUpload setCsvObject={setCsvObject} pathInfo={{ version, country, file }} />

 

해당 file의 이름을 저장해두기 위해 useState를 만든다.

//App.js

...

const App = () => {
  ...
  const [file, setFile] = useState("");

 

file 변수는 MyFileList에서 파일 이름이 변할 때 저장되어야 한다.

//MyFileList.js

...

const MyFileList = ({fileList, setFile}) => {

  return (
    <div>
      <form>
        <span>FileName : </span>
        <select onChange={(e) => setFile(e.target.value)}>
          <option value="">선택하세요.</option>
          {fileList.map((item) => getOptionsForVersion(item))}
        </select>
      </form>
    </div>
  );
};

export default MyFileList;

 

FileUpload.js에서는 pathInfo를 추가하고 uploadOptionFile 함수를 만들자.

 

이전 코드도 파일을 업로드하면 library.js의 handleUpload에서 parsingCsv가 handsontable에 csv를 보여주었다.

uploadOptionFile은 path가 정상인 경우에 fetch의 then에서 parsingCsv를 호출하도록 하면 된다.

(MY_SERVER를 사용하기 위해 nodelibrary.js에서 MY_SERVER에 export를 선언해둬야 한다.)

// FileUpload.js

...

const FileUpload = ({ setCsvObject, fileUploadFlag, flagOn, flagOff, pathInfo }) => {

  ...
  
  const uploadOptionFile = () => {
    const { version, country, file } = pathInfo;

    if(version === "" || country === "" || file === "") return;

    flagOn();
    let filePath = `${mnode.PATH}/${version}/${country}/${file}`;

    fetch(`${mnode.MY_SERVER}/getFile?path=${filePath}`)
    .then((response) => response.json())
    .then((data) => lib.parsingCsv(data.info, setCsvObject));
  }

  useEffect(uploadOptionFile, [pathInfo.file]);

(parsingCsv에 대한 설명은 (6) csv 파일 파싱하기(7) App.js에서 파싱된 csv 파일 받기 참고)

 

이때, uploadOptionFile에서 flagOn을 호출하지 않으면 아래의 에러가 발생한다.

 

MyTable.js에서 fileUploadFlag가 true인 경우만 id="hot-app"을 찾을 수 있는데,

flag가 off인 상태로 파일을 불렀기 때문에 handsontable을 create할 수 없어서 발생하는 에러다.

//MyTable.js

{fileUploadFlag && (
  <div>
    <button onClick={csvDownLoad}>DOWNLOAD</button>
    <div>
      <span>{displayIndex}</span>
      <input value={displayCell} onChange={setValueCell} />
    </div>
    <div id="hot-app"></div>
  </div>
)}

 

이제 server의 csv 파일도 불러올 수 있게 되었다.

 

최종 코드는 아래를 참고하자.

 

Node js

//server.js
const express = require('express');
const app = express();
const nodetest = require('./routes/nodetest');
const getFileFolderList = require('./routes/getFileFolderList');
const getFile = require('./routes/getFile');

const cors = require('cors');
app.use(cors()); //npm install cors --save

app.use('/nodetest', nodetest);
app.use('/getFileFolderList', getFileFolderList);
app.use('/getFile', getFile);

app.listen(3002, () => console.log('Node.js Server is running on port 3002...'));

 

// getFile.js
const express = require("express");
const fs = require("fs");
const router = express.Router();

router.get("/", (req, res) => {
  let path = req.query.path;
  let info = fs.readFileSync(`${path}`, "utf8");

  res.send({ info });
});

module.exports = router;

 

React 

//nodelibrary.js
export const MY_SERVER = `http://192.168.55.120:3002`;
export const PATH = `C:\\Users\\username\\Downloads\\TESTFILES`;

export const getFileFolderList = (path, fileExtension) => {
    fetch(`${MY_SERVER}/getFileFolderList?path=${path}&fileExtension=${fileExtension}`)
    .then((response) => response.json())
    .then((data) => console.log(data));
}

export const getFolderList = (setState, path) => {
    fetch(`${MY_SERVER}/getFileFolderList?path=${path}`)
    .then((response) => response.json())
    .then((data) => setState(data.folderList.map(list => list.name)));
}

export const getFileList = (path, fileExtension, setState) => {
    fetch(`${MY_SERVER}/getFileFolderList?path=${path}&fileExtension=${fileExtension}`)
    .then((response) => response.json())
    .then((data) => setState(data.fileList.map(list => list.name)));
}

export const getFile = (path) => { /* this is for just test */
    fetch(`${MY_SERVER}/getFile?path=${path}`)
    .then((response) => response.json())
    .then((data) => console.log(data));
}

 

//App.js
import React, { useEffect, useState } from "react";

import FileUpload from "./components/FileUpload";
import MyFileList from "./components/MyFileList";
import MyTable from "./components/MyTable";
import MyToggles from "./components/MyToggles";
import * as mnode from "./components/nodelibrary";

const csvObjectDefault = {
  HEIGHT: 0,
  WIDTH: 0,
  csv: [],
};

const nodeTest = () => {
  mnode.getFileFolderList(mnode.PATH, "csv");
  return;
}

const App = () => {
  const [csvObject, setCsvObject] = useState(csvObjectDefault);
  const [version, setVersion] = useState("");
  const [country, setCountry] = useState("");
  const [file, setFile] = useState("");
  const [fileList, setFileList] = useState([]);

  const getFileList = () => {
    if(version === "" || country === "") return;

    let path = `${mnode.PATH}/${version}/${country}`;
    mnode.getFileList(path, "csv", setFileList);
  }

  useEffect(getFileList, [version, country]);

  return (
    <div>
      <MyToggles
        version={version}
        setVersion={setVersion}
        country={country}
        setCountry={setCountry}
      />
      
      <hr style={{ borderColor: "grey" }} />
      
      <MyFileList fileList={fileList} setFile={setFile}/>
      
      <button onClick={nodeTest}>서버 연결</button>
      <button onClick={() => console.log(csvObject)}>print csv</button>
      <div className="App">
        <FileUpload setCsvObject={setCsvObject} pathInfo={{ version, country, file }} />
        <MyTable csvFile={csvObject}/>
      </div>
    </div>
  );
};

export default App;

 

//MyFileList.js
import React from "react";

const getOptionsForVersion = (item) => {
  return (
    <option key={item} value={item}>
      {item}
    </option>
  );
};

const MyFileList = ({fileList, setFile}) => {

  return (
    <div>
      <form>
        <span>FileName : </span>
        <select onChange={(e) => setFile(e.target.value)}>
          <option value="">선택하세요.</option>
          {fileList.map((item) => getOptionsForVersion(item))}
        </select>
      </form>
    </div>
  );
};

export default MyFileList;

 

// FileUpload.js
import React, { useState, useEffect } from "react";
import { connect } from "react-redux";
import { actionCreators } from "./store";
import * as lib from "./library.js";
import * as mnode from "./nodelibrary";
import "../css/FileUpload.scss";

const FileUpload = ({ setCsvObject, fileUploadFlag, flagOn, flagOff, pathInfo }) => {
  const [dropFlag, setDropFlag] = useState(false);

  const onClickFileLoad = () => {
    let my_input = document.getElementById("my-input");
    my_input.click();
  };

  const uploadOptionFile = () => {
    const { version, country, file } = pathInfo;

    if(version === "" || country === "" || file === "") return;

    flagOn();
    let filePath = `${mnode.PATH}/${version}/${country}/${file}`;

    fetch(`${mnode.MY_SERVER}/getFile?path=${filePath}`)
    .then((response) => response.json())
    .then((data) => lib.parsingCsv(data.info, setCsvObject));
  }

  useEffect(uploadOptionFile, [pathInfo.file]);

  return (
    <div>
      {fileUploadFlag && <button onClick={onClickFileLoad}>불러오기</button>}
      <input
        id="my-input"
        style={{ visibility: "hidden" }}
        type="file"
        accept=".csv"
        onChange={(e) => lib.handleUpload(e, setCsvObject, flagOn)}
      />
      {!fileUploadFlag && (
        <div
          id="drag-drop-field"
          className={dropFlag ? "in" : ""}
          onDrop={(e) => lib.handleOnDrop(e, setDropFlag, setCsvObject, flagOn)}
          onDragOver={(e) => lib.handleDragOver(e, setDropFlag)}
          onDragLeave={(e) => lib.handleOnDragLeave(e, setDropFlag)}
        >
          <p>drag & drop</p>
          <input
            type="file"
            accept=".csv"
            onChange={(e) => lib.handleUpload(e, setCsvObject, flagOn)}
          />
        </div>
      )}
    </div>
  );
};

function mapStateToProps(state, ownProps) {
  //console.log(state);
  return { fileUploadFlag: state };
}

function mapDispatchToProps(dispatch, ownProps) {
  return {
    flagOn: () => dispatch(actionCreators.flagOn()),
    flagOff: () => dispatch(actionCreators.flagOff()),
  };
}

export default connect(mapStateToProps, mapDispatchToProps)(FileUpload);

이전 - (9) csv 파일 목록 불러오기

다음 - (11) AutoSizeInput에 파일 이름 연동하기

반응형

댓글