aws

AWS S3에 파일 업로드, 다운로드, 삭제하기(feat. node.js)

hoazzinews 2024. 12. 21. 11:20

이번 시간에는 지난 시간에 구축한 AWS S3 서버를 이용해서 node.js & express app으로 파일 업로드, 삭제, 다운로드 등을 어떻게 하는지 살펴보겠습니다. 아직 S3를 구축하지 못했다면 지난 글을 참고해서 S3를 구축합니다.

 

지난 글: S3를 이용한 이미지 서버 만들기 - I

 

S3를 이용한 이미지 서버 만들기 - I

AWS S3(Amazon Simple Storage Service)는 Amazon Web Services(AWS)에서 제공하는 객체 스토리지 서비스입니다. 이번 시간에는 S3를 이용해서 이미지 서버를 만드는 방법에 대해서 살펴보겠습니다. 1. S3 검색 및

hoazzinews.tistory.com

 

1. node.js express 프로젝트 만들기

s3ex 폴더를 만듭니다.

 

> npm init 

s3ex로 이동한 후 node 프로젝트를 초기화 합니다.

 

2. 필요한 모듈 설치

필요한 모듈을 순차적으로 설치 합니다.

 

> npm install express 
express는 Node.js에서 가장 많이 사용되는 웹 프레임워크입니다. HTTP 서버 및 RESTful API를 구현하기 위해 설치합니다.


> npm install ejs 
ejs는 Embedded JavaScript Templating의 약자로, HTML 파일 내에 JavaScript 코드를 삽입하여 동적으로 콘텐츠를 렌더링할 수 있게 해주는 뷰 템플릿 엔진입니다.

 

> npm install compression 

compression은 HTTP 응답 데이터를 압축하는 미들웨어입니다. HTTP 응답을 Gzip이나 Brotli 등의 방식으로 압축하여 클라이언트로 보내면, 네트워크 대역폭을 절약하고 페이지 로딩 속도를 향상시킬 수 있습니다.


> npm install uuid4 
UUID v4는 랜덤으로 고유한 값을 생성합니다. 파일명, 사용자 ID 등에서 고유한 식별자를 생성할 때 유용합니다.

 

> npm install multer 
multer는 파일 업로드를 처리하기 위한 미들웨어입니다.


> npm install multer-s3 
multer-s3는 multer의 확장 모듈로, 파일을 AWS S3 버킷에 직접 업로드할 수 있게 해주는 라이브러리입니다. multer-s3를 사용하면 로컬 서버가 아닌 AWS S3에 파일을 저장할 수 있습니다.


> npm install @aws-sdk/client-s3 

@aws-sdk/client-s3는 AWS SDK for JavaScript (v3)의 S3 클라이언트 라이브러리입니다. 이 라이브러리는 AWS S3 서비스와 상호작용할 수 있게 해주며, 파일 업로드, 다운로드, 삭제 등 다양한 작업을 수행할 수 있습니다.

 

package.json에서 설치된 모듈을 확인할 수 있습니다.

{
  "name": "s3ex",
  "version": "1.0.0",
  "description": "",
  "license": "ISC",
  "author": "",
  "type": "commonjs",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "dependencies": {
    "@aws-sdk/client-s3": "^3.717.0",
    "compression": "^1.7.5",
    "ejs": "^3.1.10",
    "express": "^4.21.2",
    "multer": "^1.4.5-lts.1",
    "multer-s3": "^3.0.1",
    "uuid4": "^2.0.3"
  }
}

 

3. 애플리이션 실행 파일 생성

main.js를 만들고 다음과 같이 코딩합니다.

const express = require('express');
const multer = require('multer');
const app = express();
const compression = require('compression');

// view tmeplate setting START
app.set('view engine', 'ejs');
app.set('views', './view');
// view tmeplate setting END

app.get('/', (req, res) => {
    console.log('/');
    res.redirect('/home');

});

// router setting START
const homeRouter = require('./route/homeRouter');
app.use('/home', homeRouter);

const fileRouter = require('./route/fileRouter');
app.use('/file', fileRouter);
// router setting END

app.listen(3000);

 

4. router 생성

route 폴더를 만든 후 homeRouter.js, fileRouter.js를 만들고 다음과 같이 코딩합니다.

route 폴더를 만들고 라우터 파일(homeRouter.js, fileRouter.js)을 생성합니다.

 

homeRouter.js 파일

const express = require('express');
const router = express.Router();

// /home
router.get('/', (req, res) => {  
    console.log('/home');
    res.render('home');

});

module.exports = router;

 

fileRouter.js 파일

const express = require('express');
const router = express.Router();
const { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand } = require('@aws-sdk/client-s3');
const path = require('path');
const multer = require('multer');
const multerS3 = require('multer-s3');
const uuid4 = require('uuid4');

// AWS S3 설정 (AWS SDK v3)
const s3Client = new S3Client({
    region: 'ap-northeast-2',
    credentials: {
        accessKeyId: 'S3 ACCESS KEY',
        secretAccessKey: 'S3 SECRET ACCESS KEY',
    },
});

// Multer 설정: S3와 통합
const upload = multer({
    storage: multerS3({
        s3: s3Client, // v3에서 S3 클라이언트 객체 전달
        bucket: 'S3 BUCKET 이름', // S3 버킷 이름
        acl: 'public-read', // 파일을 공개적으로 읽을 수 있도록 설정
        metadata: function (req, file, cb) {
            cb(null, { fieldName: file.fieldname });
        
        },
        key: function (req, file, cb) {
            cb(null, uuid4().replaceAll('-', '') + Date.now().toString() + path.extname(file.originalname)); // 파일 이름을 고유하게 설정
        
        }
    })
});

// /file/upload
router.post('/upload', upload.single('profile'), (req, res) => {  
    console.log('/file/upload');
    
    if (!req.file) 
        return res.status(400).send('FILE UPLOAD FAIL!');

    // S3에서 업로드한 파일의 URL을 응답으로 반환
    const fileUrl = req.file.location;
    const fileName = req.file.key;

    res.render('home', {
        message: 'FILE UPLOAD SUCCESS!',
        fileUrl: fileUrl, 
        fileName: fileName

    });

});

// /file/download
router.get('/download', async (req, res) => {  
    console.log('/file/download');
    
    const fileName = req.query.file_name;

    try {
        const getObjectCommand = new GetObjectCommand({
            Bucket: 'S3 BUCKET 이름', // S3 버킷 이름
            Key: fileName,         // 다운로드할 파일의 Key
        });

        const data = await s3Client.send(getObjectCommand);

        // 파일의 MIME 타입이 정상적으로 전달되도록 설정
        const contentType = data.ContentType || 'application/octet-stream'; // 기본값은 binary stream

        // 파일을 다운로드 스트림으로 전달
        res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
        res.setHeader('Content-Type', contentType);

        // S3에서 가져온 파일을 바로 응답으로 전달
        data.Body.pipe(res); // data.Body는 ReadableStream입니다.
        
    } catch (err) {
        console.error(err);
        res.status(500).send('FILE DOWNLOAD ERROR!');
        
    }
});

// /file/delete
router.get('/delete', async (req, res) => {  
    console.log('/file/delete');
    
    const fileName = req.query.file_name;

    try {
        const deleteObjectCommand = new DeleteObjectCommand({
            Bucket: 'S3 BUCKET 이름',  // S3 버킷 이름
            Key: fileName,          // 삭제할 파일의 Key
        });

        await s3Client.send(deleteObjectCommand);

        res.render('home', {
            message: 'FILE DELETE SUCCESS!',
    
        });

    } catch (err) {
        console.error(err);

        res.render('home', {
            message: 'FILE DELETE FAIL!',
    
        });

    }

});

module.exports = router;

 

5. view 생성

view 폴더를 만든 후 home.ejs를 만들고 다음과 같이 코딩합니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>s3ex</title>

    <style>
        .uploaded_img {
            max-width: 300px;
            min-width: 300px;
        }
    </style>

    <script>
        // 삭제 요청을 서버로 보냄
        function deleteFile(fileName) {
            console.log('deleteFile()', fileName);
            location.href = '/file/delete?file_name=' + fileName;

        }

        // 다운로드 요청을 서버로 보냄
        function downloadFile(fileName) {
            console.log('downloadFile()', fileName);
            location.href = '/file/download?file_name=' + fileName;
            
        }
    </script>
</head>
<body>

    <!-- 파일 업로드 -->
    <form action="/file/upload" 
          method="post"
          enctype="multipart/form-data">
        <input type="file" name="profile">
        <input type="submit" value="FILE UPLOAD">
    </form>

    <!-- fileUrl이 존재할 때만 이미지와 파일 이름을 표시 -->
    <% if (typeof fileUrl !== 'undefined' && fileUrl !== null) { %>
        <div>
            <img class="uploaded_img" src="<%= fileUrl %>" alt="Uploaded File"/><br>
            Message: <span><%= message %></span><br>
            File URL: <span><%= fileUrl %></span><br>
            File Name: <span><%= fileName %></span><br>

            <!-- 파일 삭제 및 다운로드 버튼 -->
            <button onclick="deleteFile('<%= fileName %>')">FILE DELETE</button>
            <br>
            <button onclick="downloadFile('<%= fileName %>')">FILE DOWNLOAD</button>
        </div>

    <% } else { %>
        <% if (typeof message !== 'undefined') { %>
            Message: <span><%= message %></span><br>

        <% } else { %>
            Message: <span>NO FILE UPLOADED YET</span><br>

        <% } %>

    <% } %>

</body>
</html>

 

6. 애플리케이션 실행

애플리케이션을 실행하고 파일 업로드, 다운로드, 삭제 기능을 확인합니다.

 

> node main.js 

애플리케이션을 실행합니다.

 

브라우저에서 접속합니다.
파일을 선택하고 'FILE UPLOAD'를 클릭합니다.

 

업로드에 성공 했습니다.

 

버킷에서 업로드된 파일을 확인할 수 있습니다.

 

'FILE DOWNLOAD'를 클릭해서 파일을 다운로드합니다.

 

파일이 다운로드 됐습니다.

 

'FILE DELETE'를 클릭해서 파일을 삭제합니다.

 

버킷에서 파일이 삭제된 것을 확인할 수 있습니다.

 

이번 시간에는 node.js & express에서 s3 버킷을 이용하는 방법에 대해서 살펴봤습니다.

전체 소스 첨부합니다.

s3ex.zip
0.03MB