[날씨어때2] nordjs, express multer-파일포함 form전송 요청 받기

piper ·2024년 4월 17일
0

Project

목록 보기
13/15

이미지 파일이 포함된 폼데이터 post 요청을 express에서 받아서
프런트에 응답을 주려면 express의 multer를 사용해야 한다.

var multer = require('multer'); // express에 multer모듈 적용 (for 파일업로드)
var upload = multer({ dest: 'uploads/' })
// 입력한 파일이 uploads/ 폴더 내에 저장된다.
// multer라는 모듈이 함수라서 함수에 옵션을 줘서 실행을 시키면, 해당 함수는 미들웨어를 리턴한다.
app.post('/upload', upload.single('userfile'), function(req, res){
  res.send('Uploaded! : '+req.file); // object를 리턴함
  console.log(req.file); // 콘솔(터미널)을 통해서 req.file Object 내용 확인 가능.
});

미들웨어 upload.single('avatar')는 뒤의 function(req, res)함수가 실행되기 전에 먼저 실행.
미들웨어는 사용자가 전송한 데이터 중에서 만약 파일이 포함되어 있다면,
그 파일을 가공해서 req객체에 file 이라는 프로퍼티를 암시적으로 추가도록 약속되어 있는 함수.
upload.single('avatar') 의 매개변수 'avatar'는 form을 통해 전송되는 파일의 name속성을 가져야 함.

여기까지 해주면 파일 전송에도 성공하지만 파일명이 이진법으로 들어간다.
원본파일명으로 저장해주려면 아래와 같이 multer.disStorage를 사용해준다.

var multer = require('multer'); // multer모듈 적용 (for 파일업로드)
var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, 'uploads/') // cb 콜백함수를 통해 전송된 파일 저장 디렉토리 설정
  }
  filename: function (req, file, cb) {
    cb(null, file.originalname) // cb 콜백함수를 통해 전송된 파일 이름 설정
  }
})
var upload = multer({ storage: storage })

클라이언트 폼 전송 코드:

import styled from "styled-components";
import arrow from "../assets/arrow.png";
import { Link } from "react-router-dom";
import { useState } from "react";
import axios from "axios";

const PostForm = () => {
  const [form, setForm] = useState({
    name: "",
    title: "",
    posting: "",
  });

  const [imagefile, setImageFile] = useState(null);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setForm((prev) => ({ ...prev, [name]: value }));
  };

  const handleFileChange = (e) => {
    const file = e.target.files[0];
    setImageFile(file);
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    console.log("전송버튼");
    try {
      const formData = new FormData();

      formData.append("name", form.name);
      formData.append("title", form.title);
      formData.append("posting", form.posting);
      formData.append("image", imagefile);
      console.log(imagefile);

      const response = await axios.post(
        "http://localhost:3001/create",
        formData,
        {
          headers: {
            "Content-Type": "multipart/form-data",
          },
        }
      );

      console.log(response.data);
    } catch (error) {
      console.error("Error occurred:", error);
    }
  };

  return (
    <PageWrapper>
      <Link to="/">
        <img src={arrow} alt="Arrow" />
      </Link>
      <Content>
        <StyledForm onSubmit={handleSubmit} enctype="multipart/form-data">
          <h2>Post your dress today</h2>
          <StyledInput
            name="name"
            type="name"
            placeholder="name"
            value={form.name}
            onChange={handleChange}
          />
          <StyledInput
            name="title"
            type="text"
            placeholder="Title"
            value={form.title}
            onChange={handleChange}
          />
          <StyledInput
            name="posting"
            type="textarea"
            placeholder="Posting"
            value={form.posting}
            onChange={handleChange}
          />

          <div>
            <InputLabel htmlFor="profileImage">
              Choose what you dressed today:
            </InputLabel>
            <StyledInput
              name="file"
              type="file"
              id="file"
              onChange={handleFileChange}
            />
          </div>
          <ButtonWrapper>
            <StyledButton>Submit</StyledButton>
          </ButtonWrapper>
        </StyledForm>
      </Content>
    </PageWrapper>
  );
};

export default PostForm;

const PageWrapper = styled.div`
  background-color: orange;
  min-height: 100vh;

  img {
    width: 30px;
    margin-top: 60px;
    margin-left: 100px;
  }
`;

const Content = styled.div`
  flex-grow: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  margin-top: 60px;
`;

const StyledForm = styled.form`
  background-color: white;
  padding: 40px;
  border-radius: 10px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
`;

const StyledInput = styled.input`
  width: 100%;
  padding: 10px;
  margin-bottom: 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  box-sizing: border-box;
`;

const ButtonWrapper = styled.div`
  display: flex;
  justify-content: center;
`;

const StyledButton = styled.button`
  padding: 10px 20px;
  background-color: orange;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  font-size: 16px;
  font-weight: bold;
`;

const InputLabel = styled.label`
  margin-bottom: 10px;
  font-size: 12px;
`;

서버 코드

const express = require("express");
const multer = require("multer");
const mysql = require("mysql");
const cors = require("cors");
const app = express();

var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "uploads/"); // cb 콜백함수를 통해 전송된 파일 저장 디렉토리 설정
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname); // cb 콜백함수를 통해 전송된 파일 이름 설정
  },
});

const upload = multer({ storage: storage });

const db = mysql.createConnection({
  host: "localhost",
  user: "root",
  password: "비번",
  database: "데이타베이스이름",
});
db.connect();

app.use(cors());
app.use(express.json()); 

app.post("/create", upload.single("image"), (req, res) => {
  const { name, title, posting } = req.body;
  const image = req.file.filename; // 이미지 파일명
  console.log("req입니다:", req);
  console.log("req.body입니다:", req.body);
  console.log("image입니다:", image);
  //   res.send("올렸습니다 :" + req.file);
  console.log(req.file);

  db.query(
    `INSERT INTO topic (name, title, posting, image) VALUES (?, ?, ?, ?)`,
    [name, title, posting, image],
    (error, result) => {
      if (error) {
        throw error;
      }
      res.json(result);
    }
  );
});

app.listen(3001, () => {
  console.log("Node.js 서버가 3001번 포트에서 실행 중입니다.");
});

참고블로그:
https://wayhome25.github.io/nodejs/2017/02/21/nodejs-15-file-upload/

https://velog.io/@marrtil/node.js%EB%A1%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C%ED%95%98%EA%B3%A0-%EB%B6%88%EB%9F%AC%EC%98%A4%EA%B8%B0

profile
연습일지

0개의 댓글