[nodejs] 리모트 파일을 S3로 업로드 하기
nodejs 를 이용해 S3 파일 업로드는 성공했기 때문에.. nodejs 에 대해서 좀 더 심도있게 학습을 해야함에도 불구하고.. 일단은 원래 목적이었던 리모트의 파일을 S3로 업로드하는 것을 테스트 했다. 리모트의 파일을 다운로드해서 S3로 업로드 하는 것이 뭐가 어려운가.. 라고 생각할 수 있지만 이번 테스트의 목적은 로컬에 파일을 다운로드 하지 않고 바로 S3로 업로드하는 것이라서 처리가 가능할까 라는 생각이 먼저 들었다. lambda 에서 사용하기 위한 것이라서 로컬 다운로드는 생각조차 안했다.
다행히 aws-sdk 에서 stream 타입을 지원해서 가능할 것 같았다. 다운로드 하면서 바로 S3에 파일을 쓰면 간단하네.. 라고 생각할 수 있지만.. 근데 nodejs 를 제대로 모르면 이것도 생각처럼 간단하지도.. 쉽지도 않은 것이다. 물론 이걸 쉽게 처리하는 고수분들은 분명히 존재할 것이다. 나는 모르는…
우선은 파일 다운로드부터 해결을 해야했다. 다운로드는 axios
를 사용하기로 했다. 왜냐면.. 요즘 핫하니까! 그리고 부가적으로 시간처리를 쉽게 하기 위해서 moment
도 사용한다. 역시나 dotenv
와 aws-sdk
는 기본 사용이다. 구현 목표는 여러 개의 리모트 파일을 비동기로 S3로 다운로드하는 것이다. 그리고 모든 비동기 다운로드가 끝났을 때 이를 알 수 있도록 하는 방법을 제공하는 것이다.
먼저 env.js
파일과 .env
파일의 내용은 아래와 같다.
import dotenv from "dotenv";
dotenv.config();
AWS_ACCESS_KEY=
AWS_SECRET_KEY=
AWS_REGION=
AWS_BUCKET=
AWS_BUCKET_DIR=
package.json
파일의 내용은 아래와 같다.
{
"type": "module",
"dependencies": {
"aws-sdk": "^2.914.0",
"axios": "^0.21.1",
"dotenv": "^10.0.0",
"moment": "^2.29.1"
}
}
모든 다운로드가 끝났는지 체크하기 위해서는 Promise.all
을 사용해야 하는데.. 문제는 내가 Promise
라는 녀석과 친하지 않다는 것이다. 이틀 정도 공부를 했는데도 어렴풋이 이해가 될 듯 말 듯한 상태라고나 할까? 순차적으로 실행되는 것만 하다보니 비동기처리는.. 역시나 쉽지가 않았다. 그래도 어쨌든 이틀 정도 코드 작성과 테스트를 반복해서 원하는 대로 기능을 하는 download.js
파일을 아래와 같이 작성했다.
import "./env.js";
import Axios from "axios";
import AWS from "aws-sdk";
import stream from "stream";
import moment from "moment";
const s3 = new AWS.S3({
accessKeyId: process.env.AWS_ACCESS_KEY,
secretAccessKey: process.env.AWS_SECRET_KEY,
region: process.env.AWS_REGION
});
const uploadStream = (key) => {
var pass = new stream.PassThrough();
var params = {Bucket: process.env.AWS_BUCKET, Key: key, Body: pass};
return {
writeStream: pass,
promise: s3.upload(params).promise()
};
}
async function download(item) {
console.log("[" + moment().format('YYYY-MM-DD HH:mm:ss') + "] " + item.name + " download start");
var url = item.url;
var dest = process.env.AWS_BUCKET_DIR + "/" + item.name;
const {writeStream, promise} = uploadStream(dest);
const response = await Axios({
url,
method: 'GET',
responseType: 'stream'
});
response.data.pipe(writeStream);
await promise;
return new Promise((resolve, reject) => {
promise.then(() => {
console.log("[" + moment().format('YYYY-MM-DD HH:mm:ss') + "] " + item.name + " upload completed");
resolve(item.name + ' upload completed');
}).catch((err) => {
console.log(item.name + ' upload failed.', error.message);
reject(err.message);
});
});
}
function finish(resolve, reject)
{
console.log("finished");
}
var urls = [
{url: "https://daouoffice.com/cloud_guide/etc/daouoffice_member.pdf", name: "daouoffice_member.pdf"},
{url: "https://trek.scene7.com/is/content/TrekBicycleProducts/Asset%20Library/documents/Manuals/pdf/RS17_MISC_Domane_Assembly_Manual_KO.pdf", name: "RS17_MISC_Domane_Assembly_Manual_KO.pdf"}
];
Promise.all(urls.map(download)).then(finish);
오류가 없는 상태에서는 정상 작동하지만 리모트 파일이 존재하지 않거나 서버가 다운되었거나 하는 등의 오류 상황에서는 아마도 제대로 작동하지 않을 수도 있다. 그런 처리까지 넣기에는 내 실력이 너무도 부족하다.
참고자료
– https://stackoverflow.com/questions/37336050/pipe-a-stream-to-s3-upload
– https://stackoverflow.com/questions/43127835/how-to-wait-for-all-async-tasks-to-finish-in-node-js