2023. 7. 20. 17:25ㆍnode.js
inpa Dev 님의 블로그를 참고하여 적은 글입니다.
multer 란?
이미지나 동영상 등을 비롯한 여러가지 파일들을 멀티파트 형식으로 업로드 할 때 사용하는 미들웨어 이다.
멀트파트 형식이란 enctype 이 multipart/form-data 인 폼을 통해 업로드하는 데이터의 형식을 의미한다.
multer 문법
storage 는 저장할 공간에 대한 정보, 디스크나 메모리 저장 기능
diskStorage 는 하드디스크에 업로드 파일을 저장한다는 것
destination 은 저장할 경로
filename 은 저장할 파일명(파일명 + 날짜 + 확장자 형식)
limits 는 파일 개수나 파일 사이즈를 제한할 수 있다.
const multer = require('multer');
const fs = require('fs');
try {
fs.readdirSync('uploads'); // 폴더 확인
} catch(err) {
console.error('uploads 폴더가 없습니다. 폴더를 생성합니다.');
fs.mkdirSync('uploads'); // 폴더 생성
}
const upload = multer({
storage: multer.diskStorage({ // 저장한공간 정보 : 하드디스크에 저장
destination(req, file, done) { // 저장 위치
done(null, 'uploads/'); // uploads라는 폴더 안에 저장
},
filename(req, file, done) { // 파일명을 어떤 이름으로 올릴지
const ext = path.extname(file.originalname); // 파일의 확장자
done(null, path.basename(file.originalname, ext) + Date.now() + ext); // 파일이름 + 날짜 + 확장자 이름으로 저장
}
}),
limits: { fileSize: 5 * 1024 * 1024 } // 5메가로 용량 제한
});
// => 이렇게 설정한 upload라는 객체를 뒤에 라우터에 장착하면 된다.
multer 에 넣은 인수들은 다음과 같다.
먼저 storage 속성에 어디에 (destination) 어떤 이름으로 (filename) 저장할지를 넣었다.
두 함수의 req 매개변수에는 요청에 대한 정보, file 객체에는 업로드한 파일에 대한 정보가 있으며, done 매개변수는 함수이다.
done() 함수는 첫 번째 인수에는 에러가 있다면 에러를 넣고, 두 번째 인수에는 실제 경로나 파일 이름을 넣어 주면 된다.
즉 req 나 file 의 데이터를 가공해 done 으로 넘기는 식이다.
upload.single()
설정이 끝나면 위에서 선언한 upload 객체 변수가 생기게 되는데 이 안에 다양한 종류의 미들웨어가 존재한다.
파일을 하나만 업로드 하는 경우에는 single 미들웨어를 사용한다.
// 단순 웹페이지 get요청 들어오면 html을 띄워준다.
app.get('/upload', (req, res) => {
res.sendFile(path.join(__dirname, 'multipart.html'));
}
// 위에서 설정한 upload 객체 변수를 라우터에 장착 한다.
// 이미지 업로드 같은 동작은 특정 라우터에만 일어나기 때문에, app.use()에는 장착하지 않는 편이다.
app.post('/upload', upload.single('image'), (req, res) => { // 'image'라는 이름은 multipart.html의 <input type="file" name="image"> 에서 폼데이터 이름으로 온 것이다.
// upload.single('image')의 업로드 정보가 req.file에 넣어진다.
// <input type="text" name="title"> 의 텍스트 정보가 req.body에 넣어진다.
console.log(req.file, req.body);
res.send('ok');
})
single 미들웨어를 라우터 미들웨어 앞에 넣으면 multer 설정에 따라 파일 업로드 후 req.file 객체가 생성된다.
인수는 input 태그의 name 이나 폼데이터의 키와 일치하게 넣으면 된다.
업로드가 성공하면 결과는 req.file 객체 안에 들어간다.
req.file 객체는 다음과 같이 생김
{
fieldname: 'img',
originalname: 'hello.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: 'hello1567238581123.png',
path: 'uploads//hello1567238581123.png',
size: 44933
}
upload.array()
여러 파일을 업로드 하는 경우엔 HTML 의 input 태그에 multiple 을 쓴다.
<form name="이 폼의 이름" action="이 데이터들을 받을 파일" method="post" enctype="multipart/form-data">
<input type='file' name='many' multiple/>
</form>
그리고 미들웨어는 single 대신 array 로 교체하면 된다. 이때 업로드 정보들은 배열로 저장된다.
app.post('/upload', upload.array('many'), (req, res) => {
console.log(req.files); // 업로드 결과도 req.file 대신 req.files 배열에 들어간다
res.send('ok');
});
upload.field()
한번에 파일을 여러 개 업로드 하는게 아닌, 따로따로 업로드를 여러개 한다면, 즉 input 태그나 폼 데이터의 키가 다른 경우엔 fields 미들웨어를 사용한다.
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image1">
<input type="file" name="image2">
<input type="file" name="image3">
<input type="text" name="title">
<button type="submit">Submit</button>
</form>
이 경우엔 fields 미들웨어를 사용하고, 인수로 input 태그들의 name 들을 각각 적는다.
name 뿐만 아니라 limits 도 설정할 수 있는데 이미지를 다섯장까지만 넣는다라는 설정이다.
app.post('/upload',
upload.fields([{ name: 'image1', limits: 5 }, { name: 'image2' }, { name: 'image3' }]), // 배열 객체를 넣는다.
(req, res) => {
// 업로드 결과는 각각 req.files.image1, req.files.image2에 들어간다.
console.log(req.files.image1);
console.log(req.files.image2);
console.log(req.files.image3);
res.send('ok');
}
)
upload.none()
특수한 경우에, 파일이 아님에도 멀티파트 형식으로 업로드 하는 경우가 있다.
new FormData() 로 데이터를 보낼때도 있기 때문이다.
그 경우 none 미들웨어를 사용한다.
파일은 없지만 멀티파트 형식이기 때문에 함수가 따로 존재한다.
app.post('/upload', upload.none(), (req, res) => {
console.log(req.body.title); // 이 경우 파일을 업로드하지 않으므로 req.body만 존재한다.
res.send('ok');
});
multer-s3
사용법
게시글 이미지를 AWS S3 에 저장하고 싶을때 사용한다.
AWS S3 버킷에 이미지 파일을 저장하고, DB 엔 그 버킷의 이미지 파일 경로(이미지 주소)를 저장하고, 서버는 이 경로를 클라이언트로 응답하는 식으로 프로세스를 구축한다.
npm i multer
npm i multer-s3@2.10.0 # aws sdk v2 버젼을 사용하기에 multer-s3도 v2용으로
const express = require('express');
const multer = require('multer');
const path = require('path');
const AWS = require("aws-sdk");
const multerS3 = require('multer-s3');
const dotenv = require('dotenv');
dotenv.config();
const router = express.Router();
//* aws region 및 자격증명 설정
AWS.config.update({
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: 'ap-northeast-2',
});
//* AWS S3 multer 설정
const upload = multer({
//* 저장공간
// s3에 저장
storage: multerS3({
// 저장 위치
s3: new AWS.S3(),
bucket: 'test-bucket-inpa',
acl: "public-read",
contentType: multerS3.AUTO_CONTENT_TYPE,
key(req, file, cb) {
cb(null, `${Date.now()}_${path.basename(file.originalname)}`) // original 폴더안에다 파일을 저장
},
}),
//* 용량 제한
limits: { fileSize: 5 * 1024 * 1024 },
});
//* 로컬 multer 설정
const upload2 = multer(); // upload2.none() 용으로 사용
//* 싱글 이미지 파일 업로드 -> uploads/ 디렉토리에 이미지를 올린다.
router.post('/img', isLoggedIn, upload.single('img'), (req, res) => {
console.log(req.file);
res.json({ url: req.file.location });
});
//* 게시글 업로드
//* 프론트에서 multipart/form-data" > textarea 형식으로 넘어오기 때문에 upload2.none()형식으로 받아와 준다.
router.post('/', isLoggedIn, upload2.none(), async (req, res, next) => {
// ...
});
module.exports = router;
위에서 설정한 upload 라는 함수가 실행되면, 설정한 이름의 Bucket 에 파일을 업로드 할 수 있게 처리된다.
key 속성은 업로드하는 파일이 어떤 이름으로 버킷에 저장되는가에 대한 속성이다.
위 속성대로라면, 버킷에 업로드 되는 파일(객체) 의 이름은 현재 시각_파일명 이 되게 된다.
클라이언트에서 이미지를 업로드하고, 서버로 페이로드에 폼데이터를 담아 보내면, 유저의 AWS S3 버킷에 업로드한 파일(객체)를 저장하게 된다.
그리고 이렇게 저장된 이미지의 메타데이터를 req.file 로 받을 수 있다.
그리고 응답으로 req.file.location 정보를 보내주면, s3 객체 url 이 전달되어 이미지를 불러올 수 있다.
주의점
upload 함수는 S3 용 multer 함수 이기에 form-data 로 넘어온 text 를 따로 처리해주어야 한다.
예를 들어 게시글을 작성하고 서버에 포스팅 api 를 보낼때, 게시글에 사용된 이미지는 S3 에 저장되게 하고, 게시글 글 내용은 DB 에 저장해 FULLTEXT INDEX 처리를 해야 하기 때문이다.
//* 로컬 multer 설정
const upload2 = multer(); // upload2.none() 용으로 사용
// ...
//* 게시글 업로드
//* 프론트에서 multipart/form-data" > textarea 형식으로 넘어오기 때문에 upload2.none()형식으로 받아와 준다.
router.post('/', isLoggedIn, upload2.none(), async (req, res, next) => {
// ...
});
옵션
S3 폴더 별로 업로드
만일 보다 명확한 파일 관리를 위하여 따로 폴더를 만들어 그 폴더 안에서만 이미지 파일을 관리하고 싶다면?
multer s3 설정에서 파일명 자체에 경로를 줘서 설정하면 된다.
객체 ACL 설정하기
버킷에 파일을 업로드할때 다음과 같이 추가 프로퍼티로 acl을 지정하여 보낼 수 있게 설정할 수 있다.
보통 게시글의 이미지는 브라우저에 바로 검색해서 볼 수 있는 것이 일반적이니 public-read 로 설정하였다.
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
acl: 'public-read', // 공개
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
Cache-Control 헤더 설정
cache 를 사용하겠다는 의미, max-age 는 캐시 유효시간
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
cacheControl: 'max-age=31536000',
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
Content-Type 설정
선택적 contentType 옵션을 사용하여 파일의 Content/mime 유형을 설정할 수 있다.
기본적으로 콘텐츠 유형은 application/octet-stream 으로 설정된다.
따라서 이미지와 같은 파일을 다루기 위해서는 multer-s3 가 파일의 내용 유형을 자동으로 찾도록 multerS3.AUTO_CONTENT_TYPE 상수를 사용하여 contentType 을 지정하도록 해야 된다.
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
contentType: multerS3.AUTO_CONTENT_TYPE,
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
multer-s3-transform 을 이용한 이미지 리사이징
고용량의 이미지를 그대로 S3 에 올리는 것은 효율적이지 않다.
따라서 고용량의 이미지를 서버에서 직접 리사이징을 통해 최적화후 S3 에 올려보자
npm install multer-s3-transform sharp
const multer = require("multer");
const multerS3 = require("multer-s3-transform"); // multer-s3이 아닌 multer-s3-transform을 임포트
const sharp = require("sharp");
const ImageUpload = multer({
storage: multerS3({
s3,
bucket: "버켓경로",
contentType: multerS3.AUTO_CONTENT_TYPE,
shouldTransform: true,
transforms: [
{
id: "resized",
key: function (req, file, cb) {
let extension = path.extname(file.originalname);
cb(null, Date.now().toString() + extension);
},
transform: function (req, file, cb) {
cb(null, sharp().resize(100, 100)); // 이미지를 100x100 으로 리사이징
},
},
],
acl: "public-read-write",
}),
});
const uploadImageMulterMiddleware = ImageUpload.single("file");
주의점
multer-s3 를 require 하면 안된다. multer-s3-transform 을 require 해야한다.
나의 코드
// awsMulterModules.js
const multer = require("multer");
const AWS = require("aws-sdk");
const multerS3 = require("multer-s3");
const path = require("path");
require("dotenv").config();
AWS.config.update({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: "ap-northeast-2",
});
module.exports = multer({
storage: multerS3({
s3: new AWS.S3(),
bucket: "mini-be-bucket",
contentType: multerS3.AUTO_CONTENT_TYPE,
key(req, file, cb) {
const filename = path.basename(file.originalname).trim();
let newFilename = "";
for (let value of filename) {
if (value == "" || value === "_") {
value = "-";
}
newFilename += value;
}
cb(null, `upload/${Date.now()}-${newFilename}`);
},
}),
limits: {
fileSize: 100 * 1024 * 1024,
},
});
// posts.js
const express = require("express");
const {
createPost,
findAllPosts,
findPost,
updatePost,
deletePost,
} = require("../controllers/posts.controller");
const authMiddleware = require("../middleware/auth-middleware");
const upload = require("../multer/awsMulterModules");
const router = express.Router();
router.post("/", authMiddleware, upload.single("imgFile"), createPost);
router.get("/", findAllPosts);
router.get("/:postId", findPost);
router.put("/:postId", authMiddleware, updatePost);
router.delete("/:postId", authMiddleware, deletePost);
module.exports = router;
// posts.controller.js
const { Posts, Users } = require("../models");
const {
postSchema,
updatePostSchema,
} = require("../validations/posts-validation");
const createPost = async (req, res) => {
try {
let image = req.file.location;
if (!image) {
image = "<http://fweusdfn.html>";
}
console.log(req.file);
console.log(image);
const { title, content } = await postSchema.validateAsync(req.body);
const { userId } = res.locals.user;
const existUser = await Users.findOne({ where: { userId } });
if (!existUser) {
return res
.status(404)
.json({ errorMessage: "해당 유저가 존재하지 않습니다. " });
}
console.log(image.replace(/\\/original\\//, "/thumb/"));
const savedPost = await Posts.create({
userId,
title,
content,
name: existUser.name,
imgsrc: image.replace(/\\/original\\//, "/thumb/"),
});
return res.status(201).json({ message: "게시글을 생성하였습니다." });
} catch (error) {
console.log(error);
if (error.isJoi) {
return res.status(412).json({ errorMessage: error.message });
}
return res
.status(400)
.json({ errorMessage: "게시글 생성에 실패하였습니다. " });
}
};
참고 링크multer
multer
multer 란?
이미지나 동영상 등을 비롯한 여러가지 파일들을 멀티파트 형식으로 업로드 할 때 사용하는 미들웨어 이다.
멀트파트 형식이란 enctype 이 multipart/form-data 인 폼을 통해 업로드하는 데이터의 형식을 의미한다.
multer 문법
storage 는 저장할 공간에 대한 정보, 디스크나 메모리 저장 기능
diskStorage 는 하드디스크에 업로드 파일을 저장한다는 것
destination 은 저장할 경로
filename 은 저장할 파일명(파일명 + 날짜 + 확장자 형식)
limits 는 파일 개수나 파일 사이즈를 제한할 수 있다.
const multer = require('multer');
const fs = require('fs');
try {
fs.readdirSync('uploads'); // 폴더 확인
} catch(err) {
console.error('uploads 폴더가 없습니다. 폴더를 생성합니다.');
fs.mkdirSync('uploads'); // 폴더 생성
}
const upload = multer({
storage: multer.diskStorage({ // 저장한공간 정보 : 하드디스크에 저장
destination(req, file, done) { // 저장 위치
done(null, 'uploads/'); // uploads라는 폴더 안에 저장
},
filename(req, file, done) { // 파일명을 어떤 이름으로 올릴지
const ext = path.extname(file.originalname); // 파일의 확장자
done(null, path.basename(file.originalname, ext) + Date.now() + ext); // 파일이름 + 날짜 + 확장자 이름으로 저장
}
}),
limits: { fileSize: 5 * 1024 * 1024 } // 5메가로 용량 제한
});
// => 이렇게 설정한 upload라는 객체를 뒤에 라우터에 장착하면 된다.
multer 에 넣은 인수들은 다음과 같다.
먼저 storage 속성에 어디에 (destination) 어떤 이름으로 (filename) 저장할지를 넣었다.
두 함수의 req 매개변수에는 요청에 대한 정보, file 객체에는 업로드한 파일에 대한 정보가 있으며, done 매개변수는 함수이다.
done() 함수는 첫 번째 인수에는 에러가 있다면 에러를 넣고, 두 번째 인수에는 실제 경로나 파일 이름을 넣어 주면 된다.
즉 req 나 file 의 데이터를 가공해 done 으로 넘기는 식이다.
upload.single()
설정이 끝나면 위에서 선언한 upload 객체 변수가 생기게 되는데 이 안에 다양한 종류의 미들웨어가 존재한다.
파일을 하나만 업로드 하는 경우에는 single 미들웨어를 사용한다.
// 단순 웹페이지 get요청 들어오면 html을 띄워준다.
app.get('/upload', (req, res) => {
res.sendFile(path.join(__dirname, 'multipart.html'));
}
// 위에서 설정한 upload 객체 변수를 라우터에 장착 한다.
// 이미지 업로드 같은 동작은 특정 라우터에만 일어나기 때문에, app.use()에는 장착하지 않는 편이다.
app.post('/upload', upload.single('image'), (req, res) => { // 'image'라는 이름은 multipart.html의 <input type="file" name="image"> 에서 폼데이터 이름으로 온 것이다.
// upload.single('image')의 업로드 정보가 req.file에 넣어진다.
// <input type="text" name="title"> 의 텍스트 정보가 req.body에 넣어진다.
console.log(req.file, req.body);
res.send('ok');
})
single 미들웨어를 라우터 미들웨어 앞에 넣으면 multer 설정에 따라 파일 업로드 후 req.file 객체가 생성된다.
인수는 input 태그의 name 이나 폼데이터의 키와 일치하게 넣으면 된다.
업로드가 성공하면 결과는 req.file 객체 안에 들어간다.
req.file 객체는 다음과 같이 생김
{
fieldname: 'img',
originalname: 'hello.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: 'hello1567238581123.png',
path: 'uploads//hello1567238581123.png',
size: 44933
}
upload.array()
여러 파일을 업로드 하는 경우엔 HTML 의 input 태그에 multiple 을 쓴다.
<form name="이 폼의 이름" action="이 데이터들을 받을 파일" method="post" enctype="multipart/form-data">
<input type='file' name='many' multiple/>
</form>
그리고 미들웨어는 single 대신 array 로 교체하면 된다. 이때 업로드 정보들은 배열로 저장된다.
app.post('/upload', upload.array('many'), (req, res) => {
console.log(req.files); // 업로드 결과도 req.file 대신 req.files 배열에 들어간다
res.send('ok');
});
upload.field()
한번에 파일을 여러 개 업로드 하는게 아닌, 따로따로 업로드를 여러개 한다면, 즉 input 태그나 폼 데이터의 키가 다른 경우엔 fields 미들웨어를 사용한다.
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="image1">
<input type="file" name="image2">
<input type="file" name="image3">
<input type="text" name="title">
<button type="submit">Submit</button>
</form>
이 경우엔 fields 미들웨어를 사용하고, 인수로 input 태그들의 name 들을 각각 적는다.
name 뿐만 아니라 limits 도 설정할 수 있는데 이미지를 다섯장까지만 넣는다라는 설정이다.
app.post('/upload',
upload.fields([{ name: 'image1', limits: 5 }, { name: 'image2' }, { name: 'image3' }]), // 배열 객체를 넣는다.
(req, res) => {
// 업로드 결과는 각각 req.files.image1, req.files.image2에 들어간다.
console.log(req.files.image1);
console.log(req.files.image2);
console.log(req.files.image3);
res.send('ok');
}
)
upload.none()
특수한 경우에, 파일이 아님에도 멀티파트 형식으로 업로드 하는 경우가 있다.
new FormData() 로 데이터를 보낼때도 있기 때문이다.
그 경우 none 미들웨어를 사용한다.
파일은 없지만 멀티파트 형식이기 때문에 함수가 따로 존재한다.
app.post('/upload', upload.none(), (req, res) => {
console.log(req.body.title); // 이 경우 파일을 업로드하지 않으므로 req.body만 존재한다.
res.send('ok');
});
multer-s3
사용법
게시글 이미지를 AWS S3 에 저장하고 싶을때 사용한다.
AWS S3 버킷에 이미지 파일을 저장하고, DB 엔 그 버킷의 이미지 파일 경로(이미지 주소)를 저장하고, 서버는 이 경로를 클라이언트로 응답하는 식으로 프로세스를 구축한다.
npm i multer
npm i multer-s3@2.10.0 # aws sdk v2 버젼을 사용하기에 multer-s3도 v2용으로
const express = require('express');
const multer = require('multer');
const path = require('path');
const AWS = require("aws-sdk");
const multerS3 = require('multer-s3');
const dotenv = require('dotenv');
dotenv.config();
const router = express.Router();
//* aws region 및 자격증명 설정
AWS.config.update({
accessKeyId: process.env.S3_ACCESS_KEY_ID,
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
region: 'ap-northeast-2',
});
//* AWS S3 multer 설정
const upload = multer({
//* 저장공간
// s3에 저장
storage: multerS3({
// 저장 위치
s3: new AWS.S3(),
bucket: 'test-bucket-inpa',
acl: "public-read",
contentType: multerS3.AUTO_CONTENT_TYPE,
key(req, file, cb) {
cb(null, `${Date.now()}_${path.basename(file.originalname)}`) // original 폴더안에다 파일을 저장
},
}),
//* 용량 제한
limits: { fileSize: 5 * 1024 * 1024 },
});
//* 로컬 multer 설정
const upload2 = multer(); // upload2.none() 용으로 사용
//* 싱글 이미지 파일 업로드 -> uploads/ 디렉토리에 이미지를 올린다.
router.post('/img', isLoggedIn, upload.single('img'), (req, res) => {
console.log(req.file);
res.json({ url: req.file.location });
});
//* 게시글 업로드
//* 프론트에서 multipart/form-data" > textarea 형식으로 넘어오기 때문에 upload2.none()형식으로 받아와 준다.
router.post('/', isLoggedIn, upload2.none(), async (req, res, next) => {
// ...
});
module.exports = router;
위에서 설정한 upload 라는 함수가 실행되면, 설정한 이름의 Bucket 에 파일을 업로드 할 수 있게 처리된다.
key 속성은 업로드하는 파일이 어떤 이름으로 버킷에 저장되는가에 대한 속성이다.
위 속성대로라면, 버킷에 업로드 되는 파일(객체) 의 이름은 현재 시각_파일명 이 되게 된다.
클라이언트에서 이미지를 업로드하고, 서버로 페이로드에 폼데이터를 담아 보내면, 유저의 AWS S3 버킷에 업로드한 파일(객체)를 저장하게 된다.
그리고 이렇게 저장된 이미지의 메타데이터를 req.file 로 받을 수 있다.
그리고 응답으로 req.file.location 정보를 보내주면, s3 객체 url 이 전달되어 이미지를 불러올 수 있다.
주의점
upload 함수는 S3 용 multer 함수 이기에 form-data 로 넘어온 text 를 따로 처리해주어야 한다.
예를 들어 게시글을 작성하고 서버에 포스팅 api 를 보낼때, 게시글에 사용된 이미지는 S3 에 저장되게 하고, 게시글 글 내용은 DB 에 저장해 FULLTEXT INDEX 처리를 해야 하기 때문이다.
//* 로컬 multer 설정
const upload2 = multer(); // upload2.none() 용으로 사용
// ...
//* 게시글 업로드
//* 프론트에서 multipart/form-data" > textarea 형식으로 넘어오기 때문에 upload2.none()형식으로 받아와 준다.
router.post('/', isLoggedIn, upload2.none(), async (req, res, next) => {
// ...
});
옵션
S3 폴더 별로 업로드
만일 보다 명확한 파일 관리를 위하여 따로 폴더를 만들어 그 폴더 안에서만 이미지 파일을 관리하고 싶다면?
multer s3 설정에서 파일명 자체에 경로를 줘서 설정하면 된다.
객체 ACL 설정하기
버킷에 파일을 업로드할때 다음과 같이 추가 프로퍼티로 acl을 지정하여 보낼 수 있게 설정할 수 있다.
보통 게시글의 이미지는 브라우저에 바로 검색해서 볼 수 있는 것이 일반적이니 public-read 로 설정하였다.
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
acl: 'public-read', // 공개
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
Cache-Control 헤더 설정
cache 를 사용하겠다는 의미, max-age 는 캐시 유효시간
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
cacheControl: 'max-age=31536000',
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
Content-Type 설정
선택적 contentType 옵션을 사용하여 파일의 Content/mime 유형을 설정할 수 있다.
기본적으로 콘텐츠 유형은 application/octet-stream 으로 설정된다.
따라서 이미지와 같은 파일을 다루기 위해서는 multer-s3 가 파일의 내용 유형을 자동으로 찾도록 multerS3.AUTO_CONTENT_TYPE 상수를 사용하여 contentType 을 지정하도록 해야 된다.
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'some-bucket',
contentType: multerS3.AUTO_CONTENT_TYPE,
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
multer-s3-transform 을 이용한 이미지 리사이징
고용량의 이미지를 그대로 S3 에 올리는 것은 효율적이지 않다.
따라서 고용량의 이미지를 서버에서 직접 리사이징을 통해 최적화후 S3 에 올려보자
npm install multer-s3-transform sharp
const multer = require("multer");
const multerS3 = require("multer-s3-transform"); // multer-s3이 아닌 multer-s3-transform을 임포트
const sharp = require("sharp");
const ImageUpload = multer({
storage: multerS3({
s3,
bucket: "버켓경로",
contentType: multerS3.AUTO_CONTENT_TYPE,
shouldTransform: true,
transforms: [
{
id: "resized",
key: function (req, file, cb) {
let extension = path.extname(file.originalname);
cb(null, Date.now().toString() + extension);
},
transform: function (req, file, cb) {
cb(null, sharp().resize(100, 100)); // 이미지를 100x100 으로 리사이징
},
},
],
acl: "public-read-write",
}),
});
const uploadImageMulterMiddleware = ImageUpload.single("file");
주의점
multer-s3 를 require 하면 안된다. multer-s3-transform 을 require 해야한다.
나의 코드
// awsMulterModules.js
const multer = require("multer");
const AWS = require("aws-sdk");
const multerS3 = require("multer-s3");
const path = require("path");
require("dotenv").config();
AWS.config.update({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
region: "ap-northeast-2",
});
module.exports = multer({
storage: multerS3({
s3: new AWS.S3(),
bucket: "mini-be-bucket",
contentType: multerS3.AUTO_CONTENT_TYPE,
key(req, file, cb) {
const filename = path.basename(file.originalname).trim();
let newFilename = "";
for (let value of filename) {
if (value == "" || value === "_") {
value = "-";
}
newFilename += value;
}
cb(null, `upload/${Date.now()}-${newFilename}`);
},
}),
limits: {
fileSize: 100 * 1024 * 1024,
},
});
// posts.js
const express = require("express");
const {
createPost,
findAllPosts,
findPost,
updatePost,
deletePost,
} = require("../controllers/posts.controller");
const authMiddleware = require("../middleware/auth-middleware");
const upload = require("../multer/awsMulterModules");
const router = express.Router();
router.post("/", authMiddleware, upload.single("imgFile"), createPost);
router.get("/", findAllPosts);
router.get("/:postId", findPost);
router.put("/:postId", authMiddleware, updatePost);
router.delete("/:postId", authMiddleware, deletePost);
module.exports = router;
// posts.controller.js
const { Posts, Users } = require("../models");
const {
postSchema,
updatePostSchema,
} = require("../validations/posts-validation");
const createPost = async (req, res) => {
try {
let image = req.file.location;
if (!image) {
image = "<http://fweusdfn.html>";
}
console.log(req.file);
console.log(image);
const { title, content } = await postSchema.validateAsync(req.body);
const { userId } = res.locals.user;
const existUser = await Users.findOne({ where: { userId } });
if (!existUser) {
return res
.status(404)
.json({ errorMessage: "해당 유저가 존재하지 않습니다. " });
}
console.log(image.replace(/\\/original\\//, "/thumb/"));
const savedPost = await Posts.create({
userId,
title,
content,
name: existUser.name,
imgsrc: image.replace(/\\/original\\//, "/thumb/"),
});
return res.status(201).json({ message: "게시글을 생성하였습니다." });
} catch (error) {
console.log(error);
if (error.isJoi) {
return res.status(412).json({ errorMessage: error.message });
}
return res
.status(400)
.json({ errorMessage: "게시글 생성에 실패하였습니다. " });
}
};
참고 링크
https://inpa.tistory.com/entry/EXPRESS-%F0%9F%93%9A-multer-%EB%AF%B8%EB%93%A4%EC%9B%A8%EC%96%B4
https://velog.io/@shitaikoto/Node-Multer-S3-aws-sdk
https://velog.io/@sarifor/0006
https://gksdudrb922.tistory.com/224
https://velog.io/@wngud4950/AWS-multer-s3-upload-%EC%98%A4%EB%A5%98
'node.js' 카테고리의 다른 글
passport.js [ node.js ] (0) | 2023.07.27 |
---|---|
에러처리 미들웨어 [ express ] [ node.js ] (0) | 2023.07.22 |
JWT 토큰 [ node.js ] [ express ] (0) | 2023.07.16 |
bcrypt 사용방법 [ Javascript ] [ node.js ] (0) | 2023.07.07 |
PM2 사용 (0) | 2023.06.24 |