Nextjs 에서 MULTER로 이미지 업로드 - 1 -

column clash·2021년 10월 1일
0

Nextjs 의 Api Router 서버기능으로 multer 로
이미지 업로드하는 것을 기록해둔다.

1편은 로컬,
2편은 s3 이미지 업로드
3편은 람다로 이미지 리사이즈 하는 것을 기록해둘 예정이다.

원본글 : https://ichi.pro/ko/api-gyeongloleul-sayonghayeo-next-jse-pail-eoblodeu-144709963121712

아래와 같이 하면 파일들을 멀티, 싱글로 업로드 가능하다.

pages/imgup.tsx

import React, { useCallback, useState } from "react";
import axios from "axios";
import { UiFileInputButton } from "./components/UiFileInputButton";

const IndexPage = () => {
  const [thumb, setThumb] = useState<string[]>([]);
  const [progress, setProgress] = useState<number>(0);
  const onChange = useCallback(
    async (formData: FormData) => {
      const config = {
        headers: { "content-type": "multipart/form-data" },
        onUploadProgress: (event: { loaded: number; total: number }) => {
          setProgress(Math.round((event.loaded * 100) / event.total));
        },
      };
      axios.post<any>("/api/imgupload", formData, config).then((res) => {
        setThumb([...thumb, ...res.data]);
      });
    },
    [thumb]
  );

  return (
    <>
      <p>
        <span>이미지 업로드</span>
        <span>{progress}</span>
      </p>
      <UiFileInputButton
        label="Upload Single File"
        // allowMultipleFiles 가 false 일경우, 하나씩만 올릴 수 있다.
        allowMultipleFiles={true}
        uploadFileName="file"
        onChange={onChange}
      />
      <ul>
        {thumb &&
          thumb.map((item: string, i: number) => {
            console.log("item", item);
            return (
              <li key={i}>
                <img src={`/uploads/${item}`} width="300" alt="업로드이미지" />
              </li>
            );
          })}
      </ul>
    </>
  );
};

export default IndexPage;

pages/components/UiFileInputButton.tsx

import React from "react";

export interface IProps {
  acceptedFileTypes?: string;
  allowMultipleFiles?: boolean;
  label: string;
  onChange: (formData: FormData) => void;
  uploadFileName: string;
}

export const UiFileInputButton: React.FC<IProps> = (props) => {
  const fileInputRef = React.useRef<HTMLInputElement | null>(null);
  const formRef = React.useRef<HTMLFormElement | null>(null);

  const onClickHandler = () => {
    fileInputRef.current?.click();
  };

  const onChangeHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (!event.target.files?.length) {
      return;
    }

    const formData = new FormData();

    Array.from(event.target.files).forEach((file) => {
      formData.append(event.target.name, file);
    });

    props.onChange(formData);

    formRef.current?.reset();
  };

  return (
    <form ref={formRef}>
      <button type="button" onClick={onClickHandler}>
        {props.label}
      </button>
      <input
        accept={props.acceptedFileTypes}
        multiple={props.allowMultipleFiles}
        name={props.uploadFileName}
        onChange={onChangeHandler}
        ref={fileInputRef}
        style={{ display: "none" }}
        type="file"
      />
    </form>
  );
};

UiFileInputButton.defaultProps = {
  acceptedFileTypes: "",
  allowMultipleFiles: false,
};

api/imgupload/index.js

import nextConnect from "next-connect";
import multer from "multer";
import path from "path"
import dayjs from "dayjs";


var storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, "./public/uploads");
  },
  filename: function (req, file, cb) {
    const nowDate = dayjs(Date.now()).format("YYMMDDHHMM");
    cb(null, `${nowDate}_${file.originalname}`);
  },
  fileFilter: function (req, file, callback) {
    var ext = path.extname(file.originalname);
    if (ext !== ".png" && ext !== ".jpg" && ext !== ".jpeg") {
      return callback(new Error("PNG, JPG만 업로드하세요"));
    }
    callback(null, true);
  },
  limits: {
    fileSize: 1024 * 1024,
  },
});

const app = nextConnect({
  onError(error, req, res) {
    res
      .status(501)
      .json({ error: `Sorry something Happened! ${error.message}` });
  },
  onNoMatch(req, res) {
    res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
  },
});


var upload = multer({ storage: storage });
app.post(upload.array("file"), function (req, res) {  
  res.json(req.files.map((v)=>v.filename))
});

export default app;

export const config = {
  api: {
    bodyParser: false, // Disallow body parsing, consume as stream
  },
};
profile
풀스택 개발 중...

2개의 댓글

comment-user-thumbnail
2023년 2월 19일

build 후 npm start로 실행하면 업로드한 파일에 접근이 안 되고 404 에러가 납니다.
이거 해결방법 아시나요?

참고로 npm run dev로 하면 잘 동작합니다.
제가 찾아본 여러가지 업로드 방법 모두 동일한 오류가 나는데 next.js 설정문제인것 같은데.. @.@

1개의 답글