Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- nodejs
- JavaScript
- GIT
- Sequelize
- AWS
- cookie
- flask
- typeORM
- dfs
- jest
- 게임
- MySQL
- Python
- mongoose
- class
- 자료구조
- 공룡게임
- TypeScript
- Queue
- game
- Express
- nestjs
- OCR
- Nest.js
- Dinosaur
- MongoDB
- 정렬
- react
- Bull
Archives
- Today
- Total
포시코딩
[NestJS] How can I respond with a file and delete with interceptor 본문
728x90
[NestJS] JSON to Excel
npm install xlsx fs import * as fs from 'fs'; import * as XLSX from 'xlsx'; // ... 생략 jsonToExcel(res) { const originalJSON = { "AD": { "name": "Andorra", "phone": "376", "flag": "🇦🇩" }, "AE": { "name": "United Arab Emirates", "phone": "97", "fla
4sii.tistory.com
이번 포스팅에선
1. DB에서 typeorm을 통해 데이터 stream 받기
2. 데이터 엑셀 파일로 임시 저장 (위 링크 참고)
3. 엑셀 파일 요청한 클라이언트로 전달
4. 전달 후 interceptor를 통해 서버에 저장된 임시 엑셀 파일 제거
위 네 가지 과정에 대해 알아보겠다.
excel.service
import { Injectable } from '@nestjs/common';
import * as XLSX from 'xlsx';
import { join } from 'path';
import * as fs from 'fs';
@Injectable()
export class ExcelService {
async createExcelFile(prefix: string, data: Array<string | Buffer>) {
// 파일 작명
const currentDate = new Date();
const formattedDate = currentDate.toISOString().slice(0, 10).replace(/-/g, '');
const filename = `${prefix}_${formattedDate}.xlsx`;
// temp 임시 폴더 없다면 생성, 있다면 무시
fs.mkdirSync(join(process.cwd(), `temp`), { recursive: true });
const filePath = join(process.cwd(), `temp/${filename}`);
// filePath 위치에 엑셀 다운로드
const wb = XLSX.utils.book_new();
const newWorksheet = XLSX.utils.json_to_sheet(data);
XLSX.utils.book_append_sheet(wb, newWorksheet, 'Sheet1');
const wbOptions: any = { bookType: 'xlsx', type: 'binary' };
XLSX.writeFile(wb, filePath, wbOptions);
return { filename, filePath };
}
}
target.service
async getTargetsByExcel() {
// typeorm stream()을 통해 데이터 받기
const queryStream = await this.targetRepository
.createQueryBuilder('t')
.select(*)
.stream();
let data: Array<string | Buffer> = [];
await new Promise<void>((resolve, reject) => {
queryStream.on('data', (chunk) => {
// stream을 통해 받는 데이터 data array에 밀어넣기
data.push(chunk);
});
queryStream.on('end', () => {
// stream 종료 시 resolve()
resolve();
});
queryStream.on('error', (err) => {
reject(err);
});
});
return this.excelService.createExcelFile('target', data);
}
위 코드에선 stream으로 받는 데이터가 모두 받아진 후 엑셀로 만들기 때문에 굳이 stream을 쓸 필요가 없다고 생각한다.
target.controller
@Get('excel')
@UseInterceptors(ExcelFileCleanupInterceptor)
async getTargetsByExcel(@Res() response: Response) {
const { filename, filePath } = await this.targetService.getTargetsByExcel();
const file = fs.createReadStream(filePath);
// 파일 전달 후 interceptor에서 서버에 저장된 임시 엑셀 파일을 지우기 위해 filePath를 response.locals에 저장
response.locals.filePathToDelete = filePath;
// setHeader에서 세팅하는 filename이 client에서 다운로드 했을 때의 파일명이 된다.
response.setHeader(
'Content-Disposition',
`attachment; filename=${filename}`,
);
return file.pipe(response);
}
excel.interceptor
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, } from '@nestjs/common';
import { Observable, tap } from 'rxjs';
import * as fs from 'fs';
@Injectable()
export class ExcelFileCleanupInterceptor implements NestInterceptor {
async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
// nestjs의 생명 주기에서 request가 아닌 response에 대해서만 동작하게
const response = context.switchToHttp().getResponse();
const responseFinished = new Promise<void>((resolve) => {
response.once('finish', resolve);
});
return next.handle().pipe(
tap(async () => {
// 위 responseFinished를 통해 controller의 return이 된 후 동작하게 만든다.
await responseFinished;
const filePath = response.locals?.filePathToDelete;
if (filePath) {
try {
// fs.unlinkSync를 통해 해당 위치의 파일 제거
fs.unlinkSync(filePath);
} catch (err) {
console.error('Error deleting file:', err);
}
}
}),
);
}
}
주석으로 설명 다 달아놓음
728x90
'JavaScript' 카테고리의 다른 글
Onclick vs. AddEventListener, 버블링, 캡쳐링 (0) | 2023.07.17 |
---|---|
OpenCV.js - CV(Computer Vision) 오픈 소스 라이브러리 (0) | 2023.07.09 |
Tesseract.js - (OCR) 이미지 텍스트 인식 라이브러리 (0) | 2023.07.09 |
Cropper.js - 이미지 자르기 라이브러리 (0) | 2023.07.09 |
[Vue 3.0] data()와 methods() 대신 setup() 사용하기 (0) | 2023.06.21 |