AWS-SDK S3 Presigned URL 파일 다운로드
1. 개요
AWS SDK 를 이용하여 파일 다운로드를 위한 Presgiend URL을 생성하고 생성한 URL을 통해 파일 다운로드 구현.
2. 구성 및 구현
2.1 S3 client
Region, Secret 정보등을 세트하여 client를 생성 하고 export 한다.
// Create service client module using ES6 syntax.
import { S3Client } from "@aws-sdk/client-s3";
// Set the AWS Region.
const REGION = process.env.S3_REGION; //e.g. "us-east-1"
const accKey = process.env.S3_ACCESS_KEY;
const secretAccKey = process.env.S3_SECRET_ACCESS_KEY;
// Create an Amazon S3 service client object.
const s3Client = new S3Client({
region: REGION,
credentials: {
accessKeyId: accKey,
secretAccessKey: secretAccKey,
},
});
export default s3Client;
2.2 API (nextjs)
S3 client, presigner 를통해 Presgiend URL을 생성한다.
import { NextApiRequest, NextApiResponse } from "next";
import { GetObjectCommand } from "@aws-sdk/client-s3";
import { HttpMethod } from "@src/datas/constants";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
// 위에 생성한 s3Client의 경로
import s3Client from "lib/s3Client";
import path from "path";
const S3GetDownloadUrl = async (req: NextApiRequest, res: NextApiResponse) => {
try {
if (req.method.toUpperCase() !== HttpMethod.POST) {
throw "BAD_REQUEST";
}
let { fileName, filePath } = req.body;
let pathKey = path.join(filePath, fileName);
// windows 환경에서 path는 \를 이용하여 구분 하지만 s3에선 '/'로 구분한다.
pathKey = (pathKey as string).replaceAll("\\", "/");
console.log(`S3 download file info : ${pathKey}`);
const exp = parseInt(process.env.S3_PRESIGNED_URL_EXPIRATION_SECONDS) || 70;
const bucketParams = {
Bucket: process.env.S3_BUCKET_NAME,
Key: pathKey,
};
const getObjectCommand = new GetObjectCommand(bucketParams);
const url = await getSignedUrl(s3Client, getObjectCommand, {
expiresIn: exp,
});
console.log(`Generated url: ${url}`);
res.json({
url: url,
fileName: fileName,
filePath: filePath,
});
} catch (err) {
res.json(err);
}
};
export default S3GetDownloadUrl;
2.3 Client FileService
클라이언트단에서 API에 URL을 요청하고 다운로드를 진행하는 Service 이다. Upload, Delete등의 기능들도 같은 파일에 구현함.
export type FileAPIOptions = {
token?: any;
fileName?: string;
filePath?: string;
repository?: FileRepository;
};
export const downloadFileS3 = async (
options: FileAPIOptions,
onDownloadProgress?: (e: ProgressEvent) => void
) => {
try {
const result = await axios
.post(
`${API_URL.S3_DOWNLOAD_FILE_URL}`,
{
fileName: options.fileName,
filePath: options.filePath,
},
{
headers: {
// 유저 인증이 필요한경우
authorization: options.token,
},
}
)
.then((res) => res.data)
.catch((err) => {
throw err;
});
return await axios
.get(result.url, {
onDownloadProgress,
responseType: "blob",
})
.then((res) => {
const url = window.URL.createObjectURL(new Blob([res.data]));
const link = document.createElement("a");
link.href = url;
link.setAttribute("download", options.fileName);
document.body.appendChild(link);
link.click();
});
} catch (err) {
throw err;
}
};
2.4 UI
UI 단에서 Fileservice의 downloadFileS3 를 호출하면 된다.
const onFileDownloadClick = (e: any) => {
downloadFileS3(
{
fileName: "<your_file_name>",
filePath: "<your_file_path>",
token: "<authorization_token>",
},
// progress 처리가 필요할 경우
(e) => {
// progress
}
);
};
return (
<div>
<button onClick={onFileDownloadClick}>Download</button>
</div>
);