이전 - (11) AutoSizeInput에 파일 이름 연동하기
현재 - (12) axios post로 파일 저장하기
다음 - (13) shelljs를 이용하여 server의 파일 삭제하기
먼저 America_2020_01.csv를 적당히 복사해서 파일 크기를 늘려보자.
2500line 정도면 500KB 정도 된다.
이 파일을 test.csv 라는 이름으로 2020/America에 저장해보자.
server에 fileSave.js를 아래와 같이 만든다.
fileName은 절대 경로로 보낸다.
그리고 fileName에 저장할 content를 fileName에 저장한다.
//fileSave.js
const express = require("express");
const router = express.Router();
const fs = require("fs");
router.get("/", (req, res) => {
let fileName = req.query.fileName;
let content = req.query.file;
fs.writeFile(fileName, content, "utf8", function (error) {
console.log("write end");
});
res.send({ result: "success" });
});
module.exports = router;
MyTable.js에서 csvDownLoad를 참고하여 saveFile을 만든다.
csvDownLoad와 saveFile의 중복 코드는 따로 함수를 만들어서 중복을 줄여도 된다. 여기서는 그대로 복사한다.
const saveFile = () => {
let rows = myTable.countRows();
let cols = myTable.countCols();
let tmpTables = myTable.getData(0, 0, rows - 1, cols - 1);
let maxRow, maxCol;
maxCol = 0;
for (let r = 0; r < rows; r++) {
for (let c = cols - 1; c >= 0; c--) {
if (!(tmpTables[r][c] === "" || tmpTables[r][c] === null)) {
maxCol = maxCol < c ? c : maxCol;
break;
}
}
}
maxRow = 0;
for (let c = 0; c < cols; c++) {
for (let r = rows - 1; r >= 0; r--) {
if (!(tmpTables[r][c] === "" || tmpTables[r][c] === null)) {
maxRow = maxRow < r ? r : maxRow;
break;
}
}
}
let parsing = myTable
.getData(0, 0, maxRow, maxCol)
.map((item) => item.map((cell) => getCell(cell)));
let realTable = parsing.map((item) => item.join(",")).join("\n");
let filePath = `${mnode.PATH}/2020/America`;
let fileName = `${filePath}/test.csv`;
fetch(`${mnode.MY_SERVER}/fileSave?fileName=${fileName}&file=${realTable}`)
.then((response) => response.json())
.then((data) => console.log(data));
return;
};
test이기 때문에 절대 경로는 2020/America로 파일 이름은 test.csv로 하드코딩 하였다.
그리고 button을 추가한다.
<button onClick={saveFile}>SAVE</button>
America_2020_01.csv를 선택한 후, SAVE 버튼을 누르면 아래와 같은 에러가 나온다.
파일이 꽤 크기 때문에 get 방식으로 보내는 것은 한계가 있어 발생하는 에러다.
보통 이런 경우는 post 방식으로 파일을 보내서 해결할 수 있다.
fetch도 post를 사용할 수 있지만, 여기에서는 axios를 이용하여 보내자.
npm install axios
axios를 import하고 fetch 부분을 아래와 같이 바꾼다.
header에 "content-type" : "application/json"으로 선언한다.
//MyTable.js
...
import axios from "axios";
...
let filePath = `${mnode.PATH}/2020/America`;
let fileName = `${filePath}/test.csv`;
const config = {
header: { "content-type": "application/json" },
};
axios
.post(`${mnode.MY_SERVER}/fileSave?fileName=${fileName}`, { file: realTable }, config)
.then((response) => console.log(response));
다시 server의 fileSave를 수정한다.
content를 body의 file로 받아오기 위해서는 bodyParser가 필요하다.
npm install body-parser
그리고 bodyParser의 용량을 1MB로 넉넉하게 잡아준다.
//fileSave.js
const express = require("express");
const router = express.Router();
const fs = require("fs");
const bodyParser = require("body-parser"); //npm install body-parser
router.use(bodyParser.json({ limit: "1MB" }));
router.post("/", (req, res) => {
let fileName = req.query.fileName;
let content = req.body.file;
fs.writeFile(fileName, content, "utf8", function (error) {
console.log("write end");
});
res.send({ result: "success" });
});
module.exports = router;
2020/America에 test.csv가 그대로 저장된 것을 알 수 있다.
이제 필요한 경로에 파일을 저장하기 위해 MyTable에도 pathInfo를 추가한다.
따라서 MyTable.js에서 file → pathInfo.file로 변경해야 한다.
<MyTable csvFile={csvObject} pathInfo={{version, country, file}}/>
filePath, fileName은 pathInfo에 대해 변경하도록 한다.
이전에 pathInfo의 file로 fileName을 불러왔고, fileName은 useState를 이용해 value에 연동하였다.
let filePath = `${mnode.PATH}/${pathInfo.version}/${pathInfo.country}`;
let fileName = `${filePath}/${value}`;
이제 파일을 불러오고, 파일이름을 변경한 후, test.csv를 열어보자.
경로를 변경해도 잘 저장되는지 직접 확인해보자.
이제 file을 저장할 때, 이미 파일이 있는 경우, 덮어쓰기를 할지 체크하자.
fileList를 App.js에서 가지고 있으므로, MyTable에 넘긴다.
그리고 setFileList도 넘긴다.
<MyTable
csvFile={csvObject}
pathInfo={{ version, country, file }}
fileList={fileList}
setFileList={setFileList}
/>
saveFile의 가장 앞 부분에 아래의 코드를 추가하자.
fileList를 for ~ of로 돌면서 같은 이름이 있다면, confirm 창이 나오도록 하자.
취소를 누르면 save 하지 않는다.
//MyTable.js
...
const MyTable = ({ csvFile, fileUploadFlag, pathInfo, fileList, setFileList }) => {
...
const saveFile = () => {
for(let name of fileList) {
if(name === value) {
let answer = window.confirm(`${name}이(가) 이미 있습니다.\n바꾸시겠습니까?`);
if(answer === false) return;
}
}
마지막으로 saveFile의 axios 코드를 아래와 같이 수정한다.
response의 status가 200인 경우(OK), 새로 저장된 파일이 생긴다면 fileList를 갱신할 필요가 있다.
axios
.post(`${mnode.MY_SERVER}/fileSave?fileName=${fileName}`, { file: realTable }, config)
.then((response) => {
if(response.status === 200) mnode.getFileList(filePath, "csv", setFileList);
});
그리고 alert를 추가하여 성공여부를 알려주자.
alert(`${value} 파일이 ${pathInfo.version}/${pathInfo.country}에 저장되었습니다.`);
최종 코드는 아래와 같다.
Node js
//fileSave.js
const express = require("express");
const router = express.Router();
const fs = require("fs");
const bodyParser = require("body-parser"); //npm install body-parser
router.use(bodyParser.json({ limit: "1MB" }));
router.post("/", (req, res) => {
let fileName = req.query.fileName;
let content = req.body.file;
fs.writeFile(fileName, content, "utf8", function (error) {
console.log("write end");
});
res.send({ result: "success" });
});
module.exports = router;
//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 fileSave = require('./routes/fileSave');
const cors = require('cors');
app.use(cors()); //npm install cors --save
app.use('/nodetest', nodetest);
app.use('/getFileFolderList', getFileFolderList);
app.use('/getFile', getFile);
app.use('/fileSave', fileSave);
app.listen(3002, () => console.log('Node.js Server is running on port 3002...'));
React
//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}
pathInfo={{ version, country, file }}
fileList={fileList}
setFileList={setFileList}
/>
</div>
</div>
);
};
export default App;
//MyTable.js
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
import * as lib from "./library.js";
import * as mnode from "./nodelibrary";
import "handsontable/dist/handsontable.full.css";
import Handsontable from "handsontable";
import AutoSizeInput from "./AutoSizeInput";
import axios from "axios";
let myTable;
let currentRow, currentColumn;
const getCell = (cell) => {
if (cell === null) return ``;
return cell.includes(",") ? `"${cell}"` : `${cell}`;
};
const csvDownLoad = () => {
let rows = myTable.countRows();
let cols = myTable.countCols();
let tmpTables = myTable.getData(0, 0, rows - 1, cols - 1);
let maxRow, maxCol;
maxCol = 0;
for (let r = 0; r < rows; r++) {
for (let c = cols - 1; c >= 0; c--) {
if (!(tmpTables[r][c] === "" || tmpTables[r][c] === null)) {
maxCol = maxCol < c ? c : maxCol;
break;
}
}
}
maxRow = 0;
for (let c = 0; c < cols; c++) {
for (let r = rows - 1; r >= 0; r--) {
if (!(tmpTables[r][c] === "" || tmpTables[r][c] === null)) {
maxRow = maxRow < r ? r : maxRow;
break;
}
}
}
let parsing = myTable
.getData(0, 0, maxRow, maxCol)
.map((item) => item.map((cell) => getCell(cell)));
let realTable = parsing.map((item) => item.join(",")).join("\n");
lib.downLoadCsv(realTable);
return;
};
const MyTable = ({ csvFile, fileUploadFlag, pathInfo, fileList, setFileList }) => {
const [displayIndex, setDisplayIndex] = useState("");
const [displayCell, setDisplayCell] = useState("");
const [value, setValue] = useState("");
const saveFile = () => {
for(let name of fileList) {
if(name === value) {
let answer = window.confirm(`${name}이(가) 이미 있습니다.\n바꾸시겠습니까?`);
if(answer === false) return;
}
}
let rows = myTable.countRows();
let cols = myTable.countCols();
let tmpTables = myTable.getData(0, 0, rows - 1, cols - 1);
let maxRow, maxCol;
maxCol = 0;
for (let r = 0; r < rows; r++) {
for (let c = cols - 1; c >= 0; c--) {
if (!(tmpTables[r][c] === "" || tmpTables[r][c] === null)) {
maxCol = maxCol < c ? c : maxCol;
break;
}
}
}
maxRow = 0;
for (let c = 0; c < cols; c++) {
for (let r = rows - 1; r >= 0; r--) {
if (!(tmpTables[r][c] === "" || tmpTables[r][c] === null)) {
maxRow = maxRow < r ? r : maxRow;
break;
}
}
}
let parsing = myTable
.getData(0, 0, maxRow, maxCol)
.map((item) => item.map((cell) => getCell(cell)));
let realTable = parsing.map((item) => item.join(",")).join("\n");
let filePath = `${mnode.PATH}/${pathInfo.version}/${pathInfo.country}`;
let fileName = `${filePath}/${value}`;
const config = {
header: { "content-type": "application/json" },
};
axios
.post(`${mnode.MY_SERVER}/fileSave?fileName=${fileName}`, { file: realTable }, config)
.then((response) => {
if(response.status === 200) mnode.getFileList(filePath, "csv", setFileList);
});
alert(`${value} 파일이 ${pathInfo.version}/${pathInfo.country}에 저장되었습니다.`);
return;
};
const selectCell = () => {
let selected = myTable.getSelectedLast();
currentRow = selected[0];
currentColumn = selected[1];
if (currentRow < 0 || currentColumn < 0) return;
setDisplayCell(myTable.getValue());
setDisplayIndex(`${lib.rowToAlpha(currentColumn + 1)}${currentRow + 1}`);
};
const setValueCell = (e) => {
if (currentRow < 0 || currentColumn < 0) return;
setDisplayCell(e.target.value);
myTable.setDataAtCell(currentRow, currentColumn, e.target.value);
};
const init = (csvFile) => {
if (csvFile === undefined || csvFile.HEIGHT === 0) return;
const container = document.getElementById("hot-app");
if (myTable !== undefined) myTable.destroy();
myTable = new Handsontable(container, {
data: lib.makeTable(csvFile, 2, 3),
colHeaders: true /* column header는 보이게 설정 */,
rowHeaders: true /* row header 보이게 설정 */,
colWidths: [60, 60, 60, 60, 60, 60, 60],
wordWrap: false /* 줄 바꿈 x */,
width: "50%",
manualColumnResize: true /* column 사이즈 조절 */,
manualRowResize: true /* row 사이즈 조절 */,
manualColumnMove: true /* column move 허용 */,
manualRowMove: true /* row move 허용 */,
dropdownMenu: true /* dropdown 메뉴 설정 */,
filters: true /* 필터 기능 on */,
contextMenu: true /* cell 클릭 시 메뉴 설정 */,
licenseKey: "non-commercial-and-evaluation",
afterSelection: selectCell,
});
};
useEffect(() => {
init(csvFile);
}, [csvFile]);
useEffect(() => {
setValue(pathInfo.file);
}, [pathInfo.file]);
return (
<div>
{fileUploadFlag && (
<div>
<button onClick={csvDownLoad}>DOWNLOAD</button>
<button onClick={saveFile}>SAVE</button>
<AutoSizeInput
placeholder="파일 이름 입력"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<div>
<span>{displayIndex}</span>
<input value={displayCell} onChange={setValueCell} />
</div>
<div id="hot-app"></div>
</div>
)}
</div>
);
};
function mapStateToProps(state, ownProps) {
//console.log(state);
return { fileUploadFlag: state };
}
export default connect(mapStateToProps)(MyTable);
'개발 > Node JS' 카테고리의 다른 글
Node js, React 파일 관리 시스템 만들기 (14) (9) | 2021.07.20 |
---|---|
Node js, React 파일 관리 시스템 만들기 (13) (0) | 2021.07.19 |
Node js, React 파일 관리 시스템 만들기 (11) (0) | 2021.07.17 |
Node js, React 파일 관리 시스템 만들기 (10) (2) | 2021.07.16 |
Node js, React 파일 관리 시스템 만들기 (9) (0) | 2021.07.16 |
댓글