Download Version Data
버튼을 클릭했을 때 특정 URL에서 모든 데이터를 다운로드하는 기능을 구현하려면, 먼저 해당 URL에서 사용 가능한 모든 파일의 목록을 가져와야 합니다. 그런 다음, 각 파일에 대해 개별적으로 다운로드 요청을 수행해야 합니다.
하지만, 브라우저 보안 정책 때문에 직접적으로 다수의 파일을 동시에 다운로드하는 것은 제한됩니다. 사용자가 각 다운로드에 명시적으로 동의해야만 합니다. 그러므로, 여러 파일을 한 번에 다운로드하는 것은 자동화하기 어렵습니다.
다음은 가능한 해결책 중 하나입니다:
위의 해결책을 구현하기 위해서는 서버 측에서 추가적인 처리가 필요합니다. 하지만, 이는 클라이언트 사이드에서 해결할 수 있는 부분이 아니므로, 서버 개발자와 협력하여 이를 구현해야 합니다.
아래는 클라이언트 사이드에서 할 수 있는 onClickDownloadVersionData
함수의 예시입니다. 이 함수는 현재 서버로부터 파일 목록을 가져와서, 사용자에게 각 파일을 개별적으로 다운로드할 수 있는 옵션을 제공합니다:
const onClickDownloadVersionData = async () => {
try {
const { data } = await axios.get(`${process.env.REACT_APP_SERVER_URL}/data/list`, {
params: { serverType, dataType, dataVersion },
});
data.forEach(file => {
const url = `https://${process.env.S3_BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com/${file.Key}`;
const link = document.createElement('a');
link.href = url;
link.download = file.Key.split('/').pop();
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
} catch (error) {
message.error(`${error} failed to load data.`);
}
};
여러 파일을 압축하여 다운로드 받는 기능을 구현하기 위해서는 서버 측에서 추가 작업이 필요합니다.
이를 위해 archiver
라이브러리를 사용할 수 있습니다. archiver
는 파일과 스트림을 ZIP 형식으로 압축하는 데 사용할 수 있는 Node.js 라이브러리입니다.
npm install archiver
그런 다음, S3 버킷에서 파일들을 읽어 ZIP 파일로 압축하고 클라이언트에게 스트림으로 전송하는 함수를 S3Service
클래스에 추가할 수 있습니다. 아래는 이를 위한 예시 코드입니다:
const onClickDownloadVersionData = async () => {
if (dataVersion === '') {
Swal.fire('Check', 'Please input data version', 'question');
return;
}
try {
const body = { serverType, dataType, dataVersion };
const response = await axios.get(`${process.env.REACT_APP_SERVER_URL}/data/version`, {
params: body,
responseType: 'blob' // 파일을 Blob 형태로 받기
});
// Blob을 다운로드 링크로 변환하여 사용자가 저장할 수 있게 함
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `${dataVersion}.zip`); // 파일명 설정
document.body.appendChild(link);
link.click();
message.success('Download successful.');
} catch (error) {
message.error('Download failed: ' + error.message);
}
};
async downloadAndZipFiles(serverType: string, dataType: string, dataVersion: string, response) {
try {
const bucket = dataType === 'server' ? this.s3ServerBucket : this.s3ClientBucket;
const folderPath = `${serverType}/${dataVersion}/`;
const listParams = {
Bucket: bucket,
Prefix: folderPath
};
const listedObjects = await this.s3.send(new ListObjectsV2Command(listParams));
if (listedObjects.Contents.length === 0) {
throw new Error('No files found to zip');
}
const archive = archiver('zip', { zlib: { level: 9 } });
archive.on('error', err => {
throw err;
});
response.attachment(`${dataVersion}.zip`);
archive.pipe(response);
for (const object of listedObjects.Contents) {
const stream = await this.getObjectStream(bucket, object.Key);
archive.append(stream, { name: object.Key.split('/').pop() });
}
archive.finalize().then(() => {
response.end();
});
} catch (err) {
this.logger.error('Error', err);
response.status(500).send('Error occurred during file download');
}
}
async getObjectStream(bucket: string, key: string): Promise<Readable> {
const params = { Bucket: bucket, Key: key };
const command = new GetObjectCommand(params);
const { Body } = await this.s3.send(command);
if (Body instanceof Readable) {
return Body;
} else {
throw new Error('Failed to get a valid stream from S3');
}
}
위의 코드는 다음과 같이 작동합니다:
archiver
를 사용하여 ZIP 파일을 생성합니다.이 기능을 사용하기 위해서는 클라이언트 사이드에서도 해당 API 엔드포인트로 요청을 보내고, 응답으로 받은 스트림을 사용하여 파일을 다운로드해야 합니다. 또한, 이 기능을 사용하기 위해서는 서버의 해당 라우트에서 적절한 HTTP 헤더 설정이 필요합니다 (예: Content-Type
를 application/zip
으로 설정).
이 방법은 특히 대용량 파일이나 많은 수의 파일을 처리할 때 유용합니다. 그러나 대용량 파일의 경우 메모리 사용량과 네트워크 대역폭을 고려해야 할 수 있습니다.