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
},
};
build 후 npm start로 실행하면 업로드한 파일에 접근이 안 되고 404 에러가 납니다.
이거 해결방법 아시나요?
참고로 npm run dev로 하면 잘 동작합니다.
제가 찾아본 여러가지 업로드 방법 모두 동일한 오류가 나는데 next.js 설정문제인것 같은데.. @.@