ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Node.js] multer-s3를 이용한 AWS s3 파일 업로드
    카테고리 없음 2023. 11. 15. 11:54

    AWS s3 

    파일 저장용 클라우드 서비스

     

    사용자생성/ 엑세스 키 발급

    엑세스 키 만들기

    서버 만들 때 엑세스 키 사용함 => 엑세스 키 따로 잘 복사해서 보관해두기

    ARN 도 복사해서 보관

    s3 => 이미지나 파일을 저장할 수 있는 스토리지를 빌릴 수 있음

    버킷 생성!!

    => 버킷만들기 클릭

    버킷 접근 권한 설정

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "1",
                "Effect": "Allow",
                "Principal": "*",
                "Action": "s3:GetObject",
                "Resource": "arn:aws:s3:::버킷명/*"  <= 설정
            },
            {
                "Sid": "2",
                "Effect": "Allow",
                "Principal": {
                    "AWS": "ARN"   <= 설정
                },
                "Action": [
                    "s3:PutObject",
                    "s3:DeleteObject"
                ],
                "Resource": "arn:aws:s3:::버킷명/*"   <= 설정
            }
        ]
    }

    정책이 총2개인데

    1번은 모든 유저는 GET이 가능하고

    2번은 관리자만 PUT, DELETE가 가능하다고 적어놓은 것임

    [
        {
            "AllowedHeaders": [
                "*"
            ],
            "AllowedMethods": [
                "PUT",
                "POST"
            ],
            "AllowedOrigins": [
                "*"
            ],
            "ExposeHeaders": [
                "ETag"
            ]
        }
    ]

     

    모듈 설치

    multer, multer-s3, aws-sdk 을 설치해주자

     

    터미널에 입력

    npm install multer multer-s3 aws-sdk

    S3 객체 생성

    엑세스 키 ID와 보안 엑세스 키가 유출되면 다른 사람이 여러분의 AWS 계정을 마음대로 사용할 수 있음.
    .env 파일은 .gitignore에 추가해 깃허브 등에 올리지말고 서버에서 직접 생성해 내용을 작성하는 것이 좋음.

     

    env환경변수 설정해주기 위해서 dotenv도 설치하고 require해줌

    터미널에 입력

    npm install dotenv

     

    routes/upload.js

    require("dotenv").config()

     

    .env

    REACT_APP_S3_KEY = "엑세스키"
    REACT_APP_S3_SECRET = "보안 엑세스키"

     

     

    .gitignore에 추가

    routes/upload.js

    require("dotenv").config() 
    const multer = require('multer')
    const multerS3 = require('multer-s3')
    const { S3Client } = require('@aws-sdk/client-s3')
    const s3 = new S3Client({
        region: 'ap-northeast-2',
        credentials: {
         	accessKeyId: process.env.REACT_APP_S3_KEY,
            secretAccessKey: process.env.REACT_APP_S3_SECRET,
        }
    })
    
    const upload = multer({
        storage: multerS3({
            s3: s3,
            bucket: '버킷명',
            key: function (요청, file, cb) {
                cb(null, Date.now().toString()) //업로드시 파일명 변경가능
            }
        }),
        limits: { fileSize: 5 * 1024 * 1024 },
    })

     

    multer의 storage 옵션을 multerS3로 교체하고, multer의 옵션으로 s3 객체, 버킷명(bucket), 파일명(key)을 입력함

     

    multerS3을 사용하면 req.file.location에 S3 버킷 이미지 주소가 담겨있다

    const FileUpload = ({ setModal }) => {
    
        const [image, setImage] = useState({
            preview:
                "",
            data: ""
        });
    
        const submit = async (e) => {
            e.preventDefault();
    
            let formData = new FormData();
            let email = sessionStorage.getItem("email");
            formData.append("file", image.data);
            formData.append("email", email);
    
            console.log(image.data, "선택한이미지! ");
            console.log(sessionStorage.getItem("email"))
            console.log(image.preview, "들어간파일");
            console.log(formData.getAll('file'))
    
    
    
            try {
                await axios.post(
                    "/upload/submit",
                    formData,
                    {
                        headers: {
                            "Content-Type": "multipart/form-data",
                        },
                    },
                ).then((res) => {
                    console.log(res);
    
                    sessionStorage.setItem('location',
                        JSON.stringify(
                            res.data
                        )
    
                    )
                }
                );
            }
            catch (err) {
                console.error("Error during request:", err);
            }
            setModal(false)
        };

     

    file을 담아 보낼 땐, FormData에 담아서 보낸다

    혹시 back 서버에서 받을 때, req.body가 undefined로 나온다면 formData append 순서를 바꿔주자!

    (순서에 따라 req.body가 완전히 채워지지 않아 값이 보이지 않을 수 있음)

     

    출력은 아래와같이 해주었다.

    My.jsx

    let location = JSON.parse(sessionStorage.getItem("location"))
    
    const My = () => {
    
    return(
     	<div className={styles.userImg} style={{
                backgroundImage: `url(${location})`,
                backgroundSize: 'cover',
              }}>
    	</div>
    	)
    }

     

     

    전체코드

     

    FileUpload.jsx

    import React, { useState, useRef } from 'react'
    import axios from '../../axios'
    import styles from './FileUpload.module.css'
    
    const FileUpload = ({ setModal }) => {
    
        const [image, setImage] = useState({
            preview:
                "",
            data: ""
        });
    
        const submit = async (e) => {
            e.preventDefault();
    
            let formData = new FormData();
            let email = sessionStorage.getItem("email");
            formData.append("file", image.data);
            formData.append("email", email);
    
            console.log(image.data, "선택한이미지! ");
            console.log(sessionStorage.getItem("email"))
            console.log(image.preview, "들어간파일");
            console.log(formData.getAll('file'))
    
    
    
            try {
                await axios.post(
                    "/upload/submit",
                    formData,
                    {
                        headers: {
                            "Content-Type": "multipart/form-data",
                        },
                    },
                ).then((res) => {
                    console.log(res);
    
                    sessionStorage.setItem('location',
                        JSON.stringify(
                            res.data
                        )
    
                    )
                }
                );
            }
            catch (err) {
                console.error("Error during request:", err);
            }
            setModal(false)
        };
    
        const handleFileChange = (e) => {
            const img = {
                preview: URL.createObjectURL(e.target.files[0]),
                data: e.target.files[0],
            };
            setImage(img);
        };
    
        return (
            <div className={styles.modal} onSubmit={(e) => e.preventDefault}>
                <div className={styles.modalContent}>
                    <div className={styles.close}>
                        <button className={styles.closeBtn} onClick={() => { setModal() }}>X</button><br></br>
                    </div>
                    <label className={styles.imgLabel} htmlFor="profileImg">프로필 이미지 선택</label><br></br>
                    <input
                        type="file"
                        onChange={handleFileChange}
                        accept="Images/*"
                        id="profileImg"
                        name="file"
                    // multiple //여러장업로드 할 때
                    />
                    <img src={image.preview} />
                    <button onClick={submit} className={styles.submitBtn}>확인</button>
                </div>
            </div>
        )
    }
    
    export default FileUpload

     

    upload.js

    const express = require('express');
    const app = express();
    const router = express.Router()
    const path = require('path')
    const conn = require('../config/database');
    router.use(express.static("Images"));
    const multer = require('multer')
    const multerS3 = require('multer-s3')
    const { S3Client } = require('@aws-sdk/client-s3')
    require("dotenv").config()
    
    // try {
    //     fstat.readdirSync('uploads');
    // } catch (err) {
    //     console.err('uploads 폴더가 없어 uploads 폴더를 생성함')
    //     fstat.mkdirSync('uploads');
    // }
    
    const s3 = new S3Client({
        region: 'ap-northeast-2',
        credentials: {
            accessKeyId: process.env.REACT_APP_S3_KEY,
            secretAccessKey: process.env.REACT_APP_S3_SECRET,
        }
    })
    
    
    const upload = multer({
        storage: multerS3({
            s3: s3,
            bucket: 'contistoryprompt',
            key: function (요청, file, cb) {
                cb(null, Date.now().toString()) //업로드시 파일명 변경가능
            }
        }),
        limits: { fileSize: 5 * 1024 * 1024 },
    })
    
    router.post("/submit", upload.single("file"), (req, res) => {
        console.log(req.file, "파일");
        console.log(req.body.email)
        const user_profilpath = req.file.location;
        const user_email = req.body.email
    
        let sql = 'UPDATE t_user SET user_profilpath = ? WHERE user_email = ?'
        let sql2 = 'SELECT * FROM t_user WHERE user_email=?'
        conn.query(sql, [user_profilpath, user_email], (err, result) => {
            if (err) {
                console.log(err);
                res.
                    status(500).send("Internal Server Error");
            } else {
                conn.query(sql2, [user_email], (err, rows) => {
                    if (rows) {
                        console.log(rows)
                        res.json(rows[0].user_profilpath)
                    } else if (err) {
                        console.log(err)
                        res.status(500).send("err")
                    }
                })
            }
        });
    
    });
    
    module.exports = router;

    => DB에 저장까지 해주었다.

Designed by Tistory.