참고
- FormData와 multer로 여러 파일 업로드하기
프론트엔드(리액트)에서 이미지를 업로드한 후, 서버(Node JS)에 저장해 보자.
먼저 링크를 참고하여 ReactImageList.js를 아래와 같이 수정하자.
불필요한 내용은 지우고 서버에 업로드만 하는 메서드인 serverUpload만 구현한다.
import React, { useEffect, useState } from "react";
import Box from "@mui/material/Box";
// upload button
import { styled } from "@mui/material/styles";
import Button from "@mui/material/Button";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
const VisuallyHiddenInput = styled("input")({
clip: "rect(0 0 0 0)",
clipPath: "inset(50%)",
height: 1,
overflow: "hidden",
position: "absolute",
bottom: 0,
left: 0,
whiteSpace: "nowrap",
width: 1,
});
const serverUpload = (image) => {
}
const ReactImageList = () => {
const handleFileUpload = (e) => {
const selectedImages = e.target.files;
const uploadImageWithDelay = async () => {
serverUpload(selectedImages[0]);
for (let i = 1; i < selectedImages.length; i++) {
await new Promise((resolve) => {
setTimeout(() => {
serverUpload(selectedImages[i]);
resolve();
}, 1000); // 1초 delay
});
}
};
uploadImageWithDelay();
};
return (
<Box sx={{ m: 3 }}>
<Button
component="label"
variant="contained"
startIcon={<CloudUploadIcon />}
onChange={handleFileUpload}
>
Upload file
<VisuallyHiddenInput type="file" accept="image/*" multiple />
</Button>
</Box>
);
};
export default ReactImageList;
Node JS에서 router(/multer)를 구현하였다면, serverUpload는 아래와 같이 구현할 수 있다.
image는 handleUpload에서 넘겨받으므로, formData를 만들어서 post로 전송하면 된다.
const serverUpload = (image) => {
const formData = new FormData();
formData.append("image", image);
let server = `http://192.168.55.120:3002`;
axios
.post(`${server}/multer`, formData)
.then((res) => console.log(res.data))
.catch((error) => console.log(error));
};
multer에서 "D:\github\node-server\images" 경로에 저장하기 위해 path의 join 함수를 사용하였다.
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, path.join("D:", "github", "node-server", "images")); // 업로드 파일의 저장 위치를 설정
},
filename: (req, file, callback) => {
callback(null, `${file.originalname}`); // 파일이 저장될 때 이름 설정
},
});
파일 크기 제한을 1G로 두고 multer를 만든다.
const limits = {
files: 50,
fileSize: 1024 * 1024 * 1024, // 1G
};
const upload = multer({ storage, limits });
post에서 upload.single("image")를 이용하면 이미지를 업로드할 수 있다.
router.post("/", upload.single("image"), (req, res) => {
전체 코드는 다음과 같다.
Node JS
server.js
const express = require("express");
const app = express();
const cors = require("cors");
app.use(cors());
const multer = require("./routes/multer");
app.use("/multer", multer);
app.listen(3002, () => {
console.log("Node.js Server is running on port 3002...");
});
multer.js
const express = require("express");
const router = express.Router();
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, path.join("D:", "github", "node-server", "images")); // 업로드 파일의 저장 위치를 설정
},
filename: (req, file, callback) => {
callback(null, `${file.originalname}`); // 파일이 저장될 때 이름 설정
},
});
const limits = {
files: 50,
fileSize: 1024 * 1024 * 1024, // 1G
};
const upload = multer({ storage, limits });
router.post("/", upload.single("image"), (req, res) => {
res.json({ result: true });
return;
});
module.exports = router;
리액트
ReactImageList.js
import React from "react";
import Box from "@mui/material/Box";
// upload button
import { styled } from "@mui/material/styles";
import Button from "@mui/material/Button";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import axios from "axios";
const VisuallyHiddenInput = styled("input")({
clip: "rect(0 0 0 0)",
clipPath: "inset(50%)",
height: 1,
overflow: "hidden",
position: "absolute",
bottom: 0,
left: 0,
whiteSpace: "nowrap",
width: 1,
});
const serverUpload = (image) => {
const formData = new FormData();
formData.append("image", image);
let server = `http://192.168.55.120:3002`;
axios
.post(`${server}/multer`, formData)
.then((res) => console.log(res.data))
.catch((error) => console.log(error));
};
const ReactImageList = () => {
const handleFileUpload = (e) => {
const selectedImages = e.target.files;
const uploadImageWithDelay = async () => {
serverUpload(selectedImages[0]);
for (let i = 1; i < selectedImages.length; i++) {
await new Promise((resolve) => {
setTimeout(() => {
serverUpload(selectedImages[i]);
resolve();
}, 1000); // 1초 delay
});
}
};
uploadImageWithDelay();
e.target.value = ""; // 같은 파일 upload를 위한 처리
};
return (
<Box sx={{ m: 3 }}>
<Button
component="label"
variant="contained"
startIcon={<CloudUploadIcon />}
onChange={handleFileUpload}
>
Upload file
<VisuallyHiddenInput type="file" accept="image/*" multiple />
</Button>
</Box>
);
};
export default ReactImageList;
캡처한 이미지 업로드
이제 캡처한 이미지를 Node 서버에 업로드해 보자.
캡처한 이미지 여러 개 업로드하기를 참고하여 Capature.js를 수정하자.
handleUpload에 multer 코드를 추가하면 된다.
import React, { useState } from "react";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import axios from "axios";
const Capture = () => {
const [imageDataUrls, setImageDataUrls] = useState([]);
const handleImagePaste = (event) => {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const blob = items[i].getAsFile();
const reader = new FileReader();
reader.onloadend = () => {
const base64encoded = reader.result.split(",")[1];
// 이미지 데이터 URL을 배열에 추가
setImageDataUrls((prevUrls) => [...prevUrls, base64encoded]);
};
reader.readAsDataURL(blob);
}
}
};
const handleUpload = async () => {
if (imageDataUrls.length === 0) {
console.error("Please paste an image first.");
return;
}
for (let index = 0; index < imageDataUrls.length; index++) {
// axios multer 사용
await new Promise((resolve) => setTimeout(resolve, 1000)); // 1초 딜레이
}
};
return (
<Box sx={{ m: 3 }}>
<Button
sx={{ m: 2 }}
component="label"
variant="contained"
startIcon={<CloudUploadIcon />}
onClick={handleUpload}
>
GitHub Upload
</Button>
<div
onPaste={handleImagePaste}
style={{
border: "1px solid #ddd",
padding: "20px",
textAlign: "center",
}}
>
<h1>이미지 붙여넣기</h1>
{imageDataUrls.map((dataUrl, index) => (
<img
key={index}
src={`data:image/png;base64,${dataUrl}`}
/>
))}
</div>
</Box>
);
};
export default Capture;
업로드를 하기 위해서는 encode된 이미지가 아니라 blob이 필요하다.
blobs를 useState로 추가하고 handleImagePaste에서 추가한다.
const [imageDataUrls, setImageDataUrls] = useState([]);
const [blobs, setBlobs] = useState([]);
const handleImagePaste = (event) => {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const blob = items[i].getAsFile();
const reader = new FileReader();
reader.onloadend = () => {
const base64encoded = reader.result.split(",")[1];
// 이미지 데이터 URL을 배열에 추가
setImageDataUrls((prevUrls) => [...prevUrls, base64encoded]);
// 이미지 데이터 URL을 배열에 추가
setBlobs((prevUrls) => [...prevUrls, blob]);
};
reader.readAsDataURL(blob);
}
}
};
저장된 blobs를 handleUpload에서 formData로 전송하면 된다.
const handleUpload = async () => {
if (blobs.length === 0) {
console.error("Please paste an image first.");
return;
}
for (let index = 0; index < blobs.length; index++) {
const image = blobs[index];
const fileName = `capture_image_${index}.jpg`;
try {
const formData = new FormData();
formData.append("image", image);
formData.append("fileName", fileName);
let server = `http://192.168.55.120:3002`;
let result = await axios.post(`${server}/multer`, formData);
console.log(result);
console.log(`Image ${index + 1} uploaded successfully.`);
} catch (error) {
console.error(`Error uploading image ${index + 1}:`, error);
}
await new Promise((resolve) => setTimeout(resolve, 1000)); // 1초 딜레이
}
};
위와 같은 방식으로 하면 blob에 정해진 대로 파일 이름이 image.png로 고정된다.
따라서 multer.js에서 파일 이름에 저장한 시간을 추가하여 변경한다.
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, path.join("D:", "github", "node-server", "images")); // 업로드 파일의 저장 위치를 설정
},
filename: (req, file, callback) => {
const originalname = file.originalname;
const extension = path.extname(originalname);
const basename = path.basename(originalname, extension);
callback(null, `${basename}_${Date.now()}${extension}`); // 파일이 저장될 때 이름 설정
},
});
전체 코드는 다음과 같다.
Node JS - multer.js
const express = require("express");
const router = express.Router();
const multer = require("multer");
const path = require("path");
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, path.join("D:", "github", "node-server", "images")); // 업로드 파일의 저장 위치를 설정
},
filename: (req, file, callback) => {
const originalname = file.originalname;
const extension = path.extname(originalname);
const basename = path.basename(originalname, extension);
callback(null, `${basename}_${Date.now()}${extension}`); // 파일이 저장될 때 이름 설정
},
});
const limits = {
files: 50,
fileSize: 1024 * 1024 * 1024, // 1G
};
const upload = multer({ storage, limits });
router.post("/", (req, res) => {
upload.single("image")(req, res, (err) => {
if (err) {
console.error("Multer error:", err);
return res.status(500).json({ error: "Multer error" });
}
console.log("File uploaded successfully!");
res.json({ result: true });
});
});
module.exports = router;
리액트 - Capture.js
import React, { useState } from "react";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import CloudUploadIcon from "@mui/icons-material/CloudUpload";
import axios from "axios";
const Capture = () => {
const [imageDataUrls, setImageDataUrls] = useState([]);
const [blobs, setBlobs] = useState([]);
const handleImagePaste = (event) => {
const items = (event.clipboardData || event.originalEvent.clipboardData).items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf("image") !== -1) {
const blob = items[i].getAsFile();
const reader = new FileReader();
reader.onloadend = () => {
const base64encoded = reader.result.split(",")[1];
// 이미지 데이터 URL을 배열에 추가
setImageDataUrls((prevUrls) => [...prevUrls, base64encoded]);
// 이미지 데이터 URL을 배열에 추가
setBlobs((prevUrls) => [...prevUrls, blob]);
};
reader.readAsDataURL(blob);
}
}
};
const handleUpload = async () => {
if (blobs.length === 0) {
console.error("Please paste an image first.");
return;
}
for (let index = 0; index < blobs.length; index++) {
const image = blobs[index];
const fileName = `capture_image_${index}.jpg`;
try {
const formData = new FormData();
formData.append("image", image);
formData.append("fileName", fileName);
let server = `http://192.168.55.120:3002`;
let result = await axios.post(`${server}/multer`, formData);
console.log(result);
console.log(`Image ${index + 1} uploaded successfully.`);
} catch (error) {
console.error(`Error uploading image ${index + 1}:`, error);
}
await new Promise((resolve) => setTimeout(resolve, 1000)); // 1초 딜레이
}
};
return (
<Box sx={{ m: 3 }}>
<Button
sx={{ m: 2 }}
component="label"
variant="contained"
startIcon={<CloudUploadIcon />}
onClick={handleUpload}
>
GitHub Upload
</Button>
<div
onPaste={handleImagePaste}
style={{
border: "1px solid #ddd",
padding: "20px",
textAlign: "center",
}}
>
<h1>이미지 붙여넣기</h1>
{imageDataUrls.map((dataUrl, index) => (
<img
key={index}
src={`data:image/png;base64,${dataUrl}`}
/>
))}
</div>
</Box>
);
};
export default Capture;
'개발 > Node JS' 카테고리의 다른 글
Node JS - dayjs로 날짜, 시간 관리하기 (0) | 2024.02.22 |
---|---|
Node JS - 폴더 내부의 전체 파일 개수 구하기 (Counting the Total Number of Files In Folder) (0) | 2024.02.08 |
Node JS - exec으로 Git Push 하기 (0) | 2024.01.14 |
Node JS - PM2 ecosystem.config.js로 환경 변수 설정하기 (0) | 2023.12.25 |
Node JS - Express 중첩 라우팅 설정하기 (Setting Nested Routing) (1) | 2023.12.23 |
댓글