일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- ts-loader
- Goerlifaucet
- TypeScript
- next-connect
- keccak256
- 다중서명계약
- next.js
- S3
- 자료구조
- goerli
- incentive
- currentTarget
- methoidID
- HTMLFormElement
- set-cookie
- 자바스크립트
- CA불러오기
- scss
- webpack
- @debug
- 스마트컨트랙트
- 해쉬테이블
- Codestates
- 코딩테스트
- Blockchain
- Sass
- 블록체인
- wallet
- JavaScript
- geth
- Today
- Total
Minwook’s portfolio
백엔드 s3 upload 구현 본문
지난 프론트 img를 서버로 전송한 코드를 이어서
서버에서 받은 img를 aws s3 bucket에 올리고자 한다
과정은 크게 나누자면
1. 프론트에서 받은 formData를 fomidable로 파싱하고 서버 폴더에 저장
2. 서버에 저장한 이미지를 fs module로 buffer로 읽어온다
3. buffer를 params body에 담아서 s3 bucket으로 보낸다.
4. 이미지 전송에 성공하면 서버에 저장되었던 이미지를 지운다.
5. 파일 이름을 응답으로 내보낸다.
*추후에는 파일 이름을 응답으로 내보내지 않고 파이어베이스 데이터베이스에 저장할 것 이다.
이전 프로젝트에서 프론트에서 s3에 업로드 기능을 구현한 코드를 참고하고자 했는데
AWS.s3.upload()함수의 params body가 타입을 string 밖에 받지 않아
서버에 저장된 이미지를 읽어서 base64로 변환해서 올리면 업로드 되지않아 좀 많이 헤멧다.
처음에 시도한 코드는 aws-sdk 패키지를 이용하였지만
구현에 성공한 코드는 @aws-sdk/client-3 패키지를 이용하였다.
@aws-sdk/client-3를 사용한 코드가 좀 더 최신 버전인듯 하다.
사용된 next.js v13.2.3
// models/aws_sdk.ts
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
import formidable from "formidable";
import fs from "fs/promises"; //next에서 fs모듈을 쓰려면 fs/promise에서 가져와야한다.
const S3_BUCKET = process.env.AWS_S3_BUCKET || "";
const s3 = new S3Client({
credentials: {
accessKeyId: process.env.AWS_S3_ACCESSKEY_Id || "",
secretAccessKey: process.env.AWS_S3_SECRET_ACCESS_KEY || "",
},
region: process.env.AWS_S3_REGION || "ap-northeast-2",
});
const uploadFile = async (file: formidable.File) => {
try {
const buffer = await fs.readFile(file.filepath); //fomidable로 저정하고 읽어온 파일
const uploadParams = {
Bucket: S3_BUCKET,
Key: file.originalFilename || "",
Body: buffer,
ContentType: file.mimetype || "",
};
await s3.send(new PutObjectCommand(uploadParams)); //반환값에 url이나 filename이 없다.
return uploadParams.Key; //따라서 filename을 그대로 리턴
} catch (err) {
console.error(err);
throw new Error("S3 업로드 실패");
}
};
export default uploadFile;
filename을 그대로 리턴하기 때문에 나중에 이미지 업로드시 파일 이름을 검증하는 과정이 필요할 것 같다.
또한 이미지 url를 반환해주지 않기 때문에 업로드한 이미지에 접근하려면
`https://${process.env.AWS_S3_BUCKET}.s3.{process.env.AWS_S3_REGION}.amazonaws.com/` + encodeURIComponent(파일이름)
위와 같은 방식으로 파일 이름를 통해 url를 직접 조합해야 한다.
// models/formidable.ts
import type { NextApiRequest } from "next";
import formidable from "formidable";
import fs from "fs/promises";
import path from "path";
export const imgStoragePath = path.join(
process.cwd() + "/src" + "/public" + "/images" //이미지 저장경로
);
/** true일시 로컬에 저장 */
export const readFile = async (
req: NextApiRequest,
saveLocally: boolean = false
) => {
try {
await fs.readdir(imgStoragePath);
} catch {
await fs.mkdir(imgStoragePath);
}
const options: formidable.Options = {};
if (saveLocally) {
options.uploadDir = imgStoragePath;
options.filename = (_name, _ext, path, _form) => {
return Date.now().toString() + "_" + path.originalFilename;
};
}
return new Promise<{
fields: formidable.Fields;
files: formidable.Files;
}>((resolve, rejects) => {
const form = formidable(options);
form.parse(req, (err, fields, files) => {
if (err) {
rejects(err);
}
resolve({ fields, files });
});
});
};
// pages/api/upload.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { readFile } from "@/models/formidable";
import uploadFile from "@/models/aws_sdk";
import fs from "fs/promises";
export const config = {
api: {
bodyParser: false, //bodyParser를 꺼준다. default = true
},
};
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
const data = await readFile(req, true); //formidable parse
const file = Array.isArray(data.files.img)
? data.files.img[0]
: data.files.img;
const filename = await uploadFile(file); //s3 upload
await fs.unlink(file.filepath); //파일 삭제
return res.status(201).json({ message: filename });
} catch (err) {
console.log(err);
return res.status(500).json({ message: "ERR!" });
}
}
-추가 fs/promise가 아닌 그냥 fs모듈을 사용하려면 next.config.js를 수정해줘야 한다.
/** @type {import('next').NextConfig} */
const path = require("path");
module.exports = {
reactStrictMode: true,
publicRuntimeConfig: {},
sassOptions: {
includePaths: [path.join(__dirname, "styles")],
},
webpack: (config, { isServer }) => {
// Fixes npm packages that depend on `fs` module
if (!isServer) {
config.resolve.fallback = {
fs: false,
};
}
return config;
},
};
그러나 이 방식으로 fs모듈을 불러오면 async/await가 적용되지 않아서 fs/promise를 사용하였다.
참고
브라우저에서 Amazon S3에 사진 업로드 - AWS SDK for JavaScript
인증되지 않은 사용자의 액세스를 활성화하면 버킷과 해당 버킷의 모든 객체, 전 세계 모든 사람에게 쓰기 권한을 부여하게 됩니다. 이러한 보안 태세는 이 사례의 기본 목표에 초점을 맞추는
docs.aws.amazon.com
S3에 파일을 업로드하는 세 가지 방법
AWS SDK for Javascript v3을 이용해 next.js에서 S3에 파일 업로드를 하는 다양한 방법을 알아보자! 개요 기본 참고 자료 링크 AWS 가이드(S3 Upload) AWS 가이드(Presigned URL) 유튜브(Sam Meech-Ward) 링크로 소개한
songsong.dev
'Today I Learned' 카테고리의 다른 글
HTMLFormElement currentTarget 타입지정 (0) | 2023.03.18 |
---|---|
로컬서버 file upload구현 (1) | 2023.03.10 |
webpack ts, sass, img loader 설정 (0) | 2023.03.06 |
코딩테스트 문제풀이 소수 찾기 (0) | 2023.01.24 |
코딩테스트 문제풀이 겹치는 선분의 길이 풀이 (0) | 2023.01.10 |