S3 file upload with Presigned url
nodejs๋ฅผ ํตํด์ s3๋ก ํ์ผ ์ ๋ก๋ ๋ฐฉ๋ฒ์ s3.update() putobject ๋ฑ๋ฑ ์๋๊ฑธ๋ก ์๋ค
ํ์ง๋ง ์ฌ๋ฌ๊ฐ์ง ์ด์ ์ค ํ์ผ ์ฉ๋๋๋น ์ ๋ก๋ ์๊ฐ์ ๊ณ ๋ คํ์ฌ ๋ฐฑ์๋๋ฅผ ๊ฑฐ์น์น์๊ณ ๋ฐ๋ก s3๋ฒํท์ผ๋ก ํ์ผ์ ์ ๋ก๋ ํ ํ์๊ฐ ์๊ฒผ๋ค. ์ฉ๋์ด 100mb์ด์์ ์๋๋ผ์ ๋ฏธ๋ฆฌ์๋ช ๋(presigned) url api ์ฌ์ฉํ๊ธฐ๋ก ํ๋ค. ๋์ค์ ๊ณ ์ฉ๋ ํ์ผ์ ์ฌ๋ฆด๋์๋ multipartupload๋ฅผ ์ฌ์ฉํด์ ์์ ํ ๊ธ์ ์์ฑํ ์์ ์ด๋ค.
s3 ์ค์
๊ธฐ๋ณธ์ค์ ์ ๋๊ฐ๋ค
React+Node+S3 upload download
1.Node,S3 download upload 2.React 1.Node์ค์ aws ์ค์ aws.js const AWS = require("aws-sdk"); const multer = require("multer"); const s3 = new AWS.S3({ accessKeyId: process.env.AWS_KEY, secretAccessKey: process.env.AWS_SEC_KEY, region: "ap-northeast-2",
bae-ha.tistory.com
presignedUrl
presigned.js
const getSignedUrl = ({ key }) => {
return new Promise((resolve, reject) => {
s3.createPresignedPost(
{
Bucket: BUCKET,
Fields: {
key,
},
Expires: 30,
Conditions: [
["content-length-range", 0, 50 * 1024 ** 2],
["starts-with", "$Content-Type", ""]
],
},
(err, data) => {
if (err) reject("err");
resolve(data);
}
);
});
};
createPresignedPost๋ฅผ ์ฌ์ฉํ ์ด์ ๋ file ์ฉ๋์ ์ ํํ๊ธฐ์ํด์ ์ฌ์ฉํ์๋ค.
๊ธฐ์กด์ getSignedUrl์ ์ฃผ๋ก ์ฌ์ฉํ์์ง๋ง file์ฉ๋ ์ค์ ๊ธฐ ์๋๊ฒ ๊ฐ์์ ์ด๋ฒ์๋ createPresignedPost ์ฌ์ฉํ์๋ค.
content-length-range๋ฅผ ์ฌ์ฉํด์ ์ฉ๋์ ์ ํํ๋ค.
๊ทธ๋ฆฌ๊ณ content-type์ ๋ฃ์์ด์ ๋ ๋์ค์ s3์ ๋ก๋ํ ๋ ํ์ฅ์๋ฅผ ๋ฐ๋ก ๋ถ์ด์ง์๊ณ uuidํ์์ผ๋ก ํ์ผ์ด๋ฆ์ ์ ์ฅํ ๊ฒ์ด๋ค. ๊ทธ๋ ํด๋น ํ์ผ์ด ์ด๋ค ํ์ ์ด ์ ๊ณตํ๊ธฐ ์ํด์ ์ฌ์ฉํ์๋ค.
get presignedUrl
const files = [...ํ์ผ ๋ฐ์ดํฐ๋ค]
const getPresigned = await Promise.all(
files.map(async(file) => {
const fileKey = uuid();
const key = `{path}/${fileKey}`;
const presigned = await getSignedUrl({key})
return {imageKey, presigned}
})
)
์ด๋ ๊ฒ ํจ์๋ฅผ ์คํ์ํค๋ฉด ํ์ผ ๊ฐ์ ๋งํผ presignedUrl ์ด ์์ฑ ๋๋ค.
์์ฑ๋ ๋ฐ์ดํฐ๋ฅผ ์ด์ react์์ httpํต์ ๋ง ์งํํ๋ฉด ๋๋ค.
React
response = url ๋ฐ์ดํฐ๋ค..
const files = [...file ๋ฐ์ดํฐ๋ค]
const result = await Promise.all(
[...files].map(async (file, index) => {
const presigned = response.getPresigned[index].presigned;
const formData = new FormData();
for (const key in presigned.fields) { // *1
formData.append(key, presigned.fields[key]);
}
formData.append("Content-Type", file.type); // *2
formData.append("file", file); // *3
const result = await axios.post(presigned.url, formData);
return result;
})
์ด์ nodejs์์ ๋ฐ์์จ presignedUrl ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ s3์ ์ง์ ์ ๋ก๋๋ฅผ ํ ์ฐจ๋ก์ด๋ค.
*1 presignedUrl์ ๋ด๊ฒจ์๋ ๊ฐ์ข ์ ๋ณด๋ค์ด๋ค ์ด๊ฑธ ๋น ์ง์์ด ๋ค ๋ถ์ฌ์ค์ผํ๋ค.
for in์ ์ฌ์ฉํ๋ฉด ๊ฐ object์ key๊ฐ์ ์ ์ ์๋ค ๊ทธ ๊ธฐ์ค์ผ๋ก ๊ฐ๋ ํํ ํ ์ ์๋ค.
*2 ๊ฐ ํ์ผ์ ํ์ ์ ์ค์ ํด์ฃผ๊ธฐ ์ํด์์ด๋ค. ์์์ ๋งํ๋ content type์ค์ ์ด ์ง๊ธ ์ด ๋ถ๋ถ์ด๋ค.
*3 ๋ง์ง๋ง์ผ๋ก ํ์ผ ๋ฐ์ดํฐ๊ฐ ์ ๋ ฅ๋๋ฉด ๋์ด๋ค.