Serverless file upload to S3
동작 구조
- 먼저 lambda 서버에 s3의 올릴 url을 요청 한다.
- 업로드 할 url을 받는다.
- 받은 s3 url에다가 파일을 보내 준다.
위와 같이 업로드를 진행 해서 자바스크립트 코드에 aws의 인증키를 노출하지 않고 S3에 바로 업로드를 진행 할 수 있다.
Serverless 설치
$ npm i serverless -g
# AWS 계정 설정하기
$ serverless config credentials --provider aws --key <ACCESS KEY ID> --secret <SECRET KEY>
Serverless 기본 프로젝트 만들기
$ serverless create --template aws-nodejs -p serverless-file-upload
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/home/gyuha/workspace/serverless-file-upload"
_______ __
| _ .-----.----.--.--.-----.----| .-----.-----.-----.
| |___| -__| _| | | -__| _| | -__|__ --|__ --|
|____ |_____|__| \___/|_____|__| |__|_____|_____|_____|
| | | The Serverless Application Framework
| | serverless.com, v1.30.3
-------'
$ cd serverless-file-upload
필요한 패키지 추가해 주기
$ npm install --save aws-sdk
여기서는 s3용 주소를 받기 위해서 aws-sdk를 설치 한다.
Serverless Offline 설정 하기
개발 할 때 마다 lambda에 올려서 테스트 하기 번거롭다.. aws에서 제공하는 SAM도 있지만, docker를 사용해서 동작하는 구조라서 지나치게 느려서 serverless의 offline plugin을 사용했다.
먼저 패키지를 추가해 준다.
$ npm install serverless-offline --save-dev
serverless.yml
파일에 아래 내용을 추가 해 줍니다.
plugins:
- serverless-offline
handler.js 파일 작성 하기
'use strict';
const AWS = require('aws-sdk');
module.exports.s3upload = async (event, context) => {
const s3 = new AWS.S3();
const params = JSON.parse(event.body);
const s3Params = {
Bucket: 'serverless-upload-qwer1',
Key: params.name,
ContentType: params.type,
ACL: 'public-read'
}
const uploadURL = s3.getSignedUrl('putObject', s3Params)
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify({
uploadURL
}),
};
return response
};
동작 테스트 하기
$ serverless offline start
로컬 서버를 띄우고.. 아래과 같이 실행해서 정상적으로 문장이 나오면 됨.
$ curl -d '{"name": "test.jpg", "type": "image/jpeg"}' -H "Content-Type: application/json" -X POST http://localhost:3000/s3upload
웹브라우저 테스트
파이썬이 깔려 있다면, 아래와 같이 간단하게 서버를 만들 수 있다.
$ python3 -m http.server 9000
s3upload.html
파일을 만들고 아래 내용을 넣어 줍니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<title>파일 업로드 데모</title>
<link rel="stylesheet" href="//fonts.googleapis.com/earlyaccess/nanumgothic.css">
<style>
html, body {
height: 100%;
margin: 0;
}
body {
font-family: 'Nanum Gothic', Helvetica, Arial, sans-serif;
}
.aligner {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
#drop {
height: 100px;
width: 200px;
border-radius: 5px;
color: #888;
background-color: #eee;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
border: dotted #777;
}
#drop:hover {
background-color: #fff;
}
</style>
</head>
<body>
<div class="aligner">
<div id="drop">파일을 여기로 드래그</div>
<div>
<h1>업로드 된 파일:</h1>
<ul id="list">
</ul>
</div>
</div>
<script type="text/javascript">
var drop = document.getElementById('drop');
var list = document.getElementById('list');
var apiBaseURL = "http://127.0.0.1:3000";
function cancel(e) {
e.preventDefault();
return false;
}
function handleDrop(e){
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
for (var i=0; i<files.length; i++) {
var file = files[i];
var reader = new FileReader();
reader.addEventListener('loadend', function(e){
fetch(apiBaseURL+"/s3upload", {
method: "POST",
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: file.name,
type: file.type
})
})
.then(function(response){
return response.json();
})
.then(function(json){
return fetch(json.uploadURL, {
method: "PUT",
body: new Blob([reader.result], {type: file.type})
})
})
.then(function(){
var uploadedFileNode = document.createElement('li');
uploadedFileNode.innerHTML = '<a href="//s3.amazonaws.com/slsupload/'+ file.name +'">'+ file.name +'</a>';
list.appendChild(uploadedFileNode);
});
});
reader.readAsArrayBuffer(file);
}
return false;
}
// Tells the browser that we *can* drop on this target
drop.addEventListener('dragenter', cancel);
drop.addEventListener('dragover', cancel);
drop.addEventListener('drop', handleDrop);
</script>
</body>
</html>
간단하게 아래와 같이 접속해서 파일 업로드를 테스트 해 보면 된다.
S3및 lambda의 권한 주기
아래는 테스트를 위해서 권한을 지나치게 주었다. 실 사용시에는 조정해서 사용해야 한다.
아래 내용은 Serverless.yml
파일 이다.
service: s3imageupload
plugins:
- serverless-offline
provider:
name: aws
runtime: nodejs8.10
region: ap-northeast-2
iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:*"
Resource: "arn:aws:s3:::serverless-upload-qwer1/*"
functions:
s3upload:
handler: handler.s3upload
events:
- http:
method: post
path: s3upload
cors:
origins:
- '*'
headers:
- '*'
allowCredentials: false
resources:
Resources:
ImageUpload:
Type: AWS::S3::Bucket
Properties:
BucketName: serverless-upload-qwer1
AccessControl: PublicRead
CorsConfiguration:
CorsRules:
- AllowedMethods:
- GET
- PUT
- POST
- HEAD
AllowedOrigins:
- "*"
AllowedHeaders:
- "*"
S3의 BucketName
은 기존에 사용하고 있는 이름이면 안 되는 경우가 있다. 중복되지 않도록 이름 짓기를 해야 한다.
배포 하기
$ sls deploy
로그 확인 하기
$ sls logs -f <function_name> --tail
위와 같이 해 놓으면 lambda의 요청에 대한 로그를 볼 수 있다.
위 내용은 github에도 올려져 있다.
댓글