Maison >interface Web >js tutoriel >Rationalisez les téléchargements de fichiers dans NestJS : analyse efficace en mémoire pour CSV et XLSX sans stockage sur disque
Analyse de fichiers sans effort dans NestJS : gérez les téléchargements CSV et XLSX en mémoire pour plus de vitesse, de sécurité et d'évolutivité
Gérer les téléchargements de fichiers dans une application Web est une tâche courante, mais gérer différents types de fichiers et s'assurer qu'ils sont traités correctement peut s'avérer difficile. Souvent, les développeurs doivent analyser les fichiers téléchargés sans les enregistrer sur le serveur, ce qui est particulièrement important pour réduire les coûts de stockage du serveur et garantir que les données sensibles ne sont pas inutilement conservées. Dans cet article, nous passerons en revue le processus de création d'un module NestJS personnalisé pour gérer les téléchargements de fichiers spécifiquement pour les fichiers CSV et XLS/XLSX, et nous analyserons ces fichiers en mémoire à l'aide des flux Node.js, afin qu'aucun fichier statique ne soit généré. créé sur le serveur.
NestJS est un framework Node.js progressif qui exploite TypeScript et fournit une architecture d'application prête à l'emploi qui vous permet de créer des applications hautement testables, évolutives, faiblement couplées et faciles à maintenir. En utilisant NestJS, nous pouvons profiter de sa structure modulaire, de son puissant système d'injection de dépendances et de son vaste écosystème.
Avant de plonger dans le code, mettons en place un nouveau projet NestJS. Si vous ne l'avez pas déjà fait, installez la CLI NestJS :
npm install -g @nestjs/cli
Créez un nouveau projet NestJS :
nest new your-super-name
Naviguez dans le répertoire du projet :
cd your-super-name
Nous devrons installer des packages supplémentaires pour gérer le téléchargement et l'analyse des fichiers :
npm install @nestjs/platform-express multer exceljsfile-type
Pour personnaliser le processus de téléchargement de fichiers, nous allons créer un moteur de stockage Multer personnalisé. Ce moteur garantira que seuls les fichiers CSV et XLS/XLSX sont acceptés, les analysera en mémoire à l'aide des flux Node.js et renverra les données analysées sans enregistrer aucun fichier sur le disque.
Créez un nouveau fichier pour notre moteur :
import { PassThrough } from 'stream'; import * as fileType from 'file-type'; import { BadRequestException } from '@nestjs/common'; import { Request } from 'express'; import { Workbook } from 'exceljs'; import { createParserCsvOrXlsx } from './parser-factory.js'; const ALLOWED_MIME_TYPES = [ 'text/csv', 'application/vnd.ms-excel', 'text/comma-separated-values', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel', ] as const; export class CsvOrXlsxMulterEngine { private destKey: string; private maxFileSize: number; constructor(opts: { destKey: string; maxFileSize: number }) { this.destKey = opts.destKey; this.maxFileSize = opts.maxFileSize; } async _handleFile(req: Request, file: any, cb: any) { try { const contentLength = Number(req.headers['content-length']); if ( typeof contentLength === 'number' && contentLength > this.maxFileSize ) { throw new Error(`Max file size is ${this.maxFileSize} bytes.`); } const fileStream = await fileType.fileTypeStream(file.stream); const mime = fileStream.fileType?.mime ?? file.mimetype; if (!ALLOWED_MIME_TYPES.includes(mime)) { throw new BadRequestException('File must be *.csv or *.xlsx'); } const replacementStream = new PassThrough(); fileStream.pipe(replacementStream); const parser = createParserCsvOrXlsx(mime); const data = await parser.read(replacementStream); cb(null, { [this.destKey]: mime === 'text/csv' ? data : (data as Workbook).getWorksheet(), }); } catch (error) { cb(error); } } _removeFile(req: Request, file: any, cb: any) { cb(null); } }
Ce moteur de stockage personnalisé vérifie le type MIME du fichier et garantit qu'il s'agit d'un fichier CSV ou XLS/XLSX. Il traite ensuite le fichier entièrement en mémoire à l'aide des flux Node.js, donc aucun fichier temporaire n'est créé sur le serveur. Cette approche est à la fois efficace et sécurisée, notamment lorsqu'il s'agit de données sensibles.
L'usine d'analyseurs est chargée de déterminer l'analyseur approprié en fonction du type de fichier.
Créez un nouveau fichier pour notre analyseur :
import excel from 'exceljs'; export function createParserCsvOrXlsx(mime: string) { const workbook = new excel.Workbook(); return [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-excel', ].includes(mime) ? workbook.xlsx : workbook.csv; }
Cette fonction d'usine vérifie le type MIME et renvoie l'analyseur approprié (soit xlsx, soit csv).
Ensuite, créons un contrôleur pour gérer les téléchargements de fichiers à l'aide de notre moteur de stockage personnalisé.
Générer un nouveau contrôleur :
nest g controller files
Dans files.controller.ts, configurez le téléchargement de fichiers à l'aide de Multer et du moteur de stockage personnalisé :
import { Controller, Post, UploadedFile, UseInterceptors, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { Worksheet } from 'exceljs'; import { CsvOrXlsxMulterEngine } from '../../shared/multer-engines/csv-xlsx/engine.js'; import { FilesService } from './files.service.js'; const MAX_FILE_SIZE_IN_MiB = 1000000000; // Only for test @Controller('files') export class FilesController { constructor(private readonly filesService: FilesService) {} @UseInterceptors( FileInterceptor('file', { storage: new CsvOrXlsxMulterEngine({ maxFileSize: MAX_FILE_SIZE_IN_MiB, destKey: 'worksheet', }), }), ) @Post() create(@UploadedFile() data: { worksheet: Worksheet }) { return this.filesService.format(data.worksheet); } }
Ce contrôleur configure un point de terminaison pour gérer les téléchargements de fichiers. Le fichier téléchargé est traité par CsvOrXlsxMulterEngine et les données analysées sont renvoyées dans la réponse sans jamais être enregistrées sur le disque.
Enfin, nous devons mettre en place un module pour inclure notre contrôleur.
Générer un nouveau module :
nest g module files
Dans les fichiers.module.ts, importez le contrôleur :
import { Module } from '@nestjs/common'; import { FilesController } from './files.controller.js'; import { FilesService } from './files.service.js'; @Module({ providers: [FilesService], controllers: [FilesController], }) export class FilesModule {}
Assurez-vous d'importer ce module dans votre AppModule :
Pour tester la fonctionnalité de téléchargement de fichiers, nous pouvons créer une simple page HTML qui permet aux utilisateurs de télécharger des fichiers CSV ou XLS/XLSX. Cette page enverra le fichier à notre point de terminaison /api/files, où il sera analysé et traité en mémoire.
Voici le fichier HTML de base pour tester le téléchargement du fichier :
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>File Upload</title> </head> <body> <h1>Upload a File (CSV or XLSX)</h1> <form action="/api/files" method="post" enctype="multipart/form-data"> <label for="file">Choose file:</label> <input type="file" id="file" name="file" accept=".csv, .xlsx" required> <br><br> <button type="submit">Upload</button> </form> </body> </html>
Pour afficher la page HTML pour les téléchargements de fichiers, nous devons d'abord installer un module NestJS supplémentaire appelé @nestjs/serve-static. Vous pouvez le faire en exécutant la commande suivante :
npm install @nestjs/serve-static
Après l'installation, nous devons configurer ce module dans AppModule :
import { Module } from '@nestjs/common'; import { join } from 'path'; import { ServeStaticModule } from '@nestjs/serve-static'; import { FilesModule } from './modules/files/files.module.js'; @Module({ imports: [ FilesModule, ServeStaticModule.forRoot({ rootPath: join(new URL('..', import.meta.url).pathname, 'public'), serveRoot: '/', }), ], }) export class AppModule {}
Cette configuration nous permettra de servir des fichiers statiques à partir du répertoire public. Maintenant, nous pouvons ouvrir la page de téléchargement de fichiers en accédant à http://localhost:3000 dans votre navigateur.
Téléchargez votre fichier
파일을 업로드하려면 다음 단계를 따르세요.
파일이 성공적으로 업로드되면 파일이 업로드되고 포맷되었다는 확인 메시지가 표시됩니다.
참고: 업로드된 파일의 형식을 지정하는 코드는 포함하지 않았습니다. 이는 CSV 또는 XLS/XLSX 파일을 처리하기 위해 선택한 라이브러리에 따라 다르기 때문입니다. GitHub에서 전체 구현을 볼 수 있습니다.
인메모리 파일 처리의 장단점 비교
인메모리 파일 처리를 사용할지 디스크에 파일을 저장할지 결정할 때 장단점을 이해하는 것이 중요합니다.
디스크에 임시 파일 없음:
더 빠른 처리:
단순화된 정리:
메모리 사용량:
파일 크기 제한:
오류 처리의 복잡성:
소형에서 중형 파일: 애플리케이션이 상대적으로 작은 파일을 처리하는 경우 인메모리 처리가 속도와 단순성을 제공할 수 있습니다.
보안에 민감한 애플리케이션: 디스크에 저장하면 안 되는 민감한 데이터를 처리할 때 메모리 내 처리를 통해 데이터 유출 위험을 줄일 수 있습니다.
고성능 시나리오: 높은 처리량과 최소 지연 시간이 필요한 애플리케이션은 인메모리 처리 오버헤드 감소의 이점을 누릴 수 있습니다.
대용량 파일: 애플리케이션이 매우 큰 파일을 처리해야 하는 경우 메모리 부족을 방지하기 위해 디스크 기반 처리가 필요할 수 있습니다.
리소스가 제한된 환경: 서버 메모리가 제한된 경우 디스크에서 파일을 처리하면 메모리 고갈을 방지하고 리소스 관리를 향상할 수 있습니다.
지속적인 저장 요구: 감사, 백업 또는 향후 검색을 위해 업로드된 파일의 복사본을 보관해야 하는 경우 파일을 디스크에 저장해야 합니다.
외부 스토리지 서비스와의 통합: 대용량 파일의 경우 AWS S3, Google Cloud와 같은 외부 스토리지 서비스에 업로드하는 것이 좋습니다.
확장성: 클라우드 스토리지 솔루션은 대용량 파일을 처리하고 중복성을 제공하여 데이터가 안전하고 여러 지리적 위치에서 쉽게 액세스할 수 있도록 보장합니다.
비용 효율성: 클라우드 스토리지를 사용하면 로컬 서버 리소스의 필요성이 줄어들고 종량제 가격이 제공되므로 대용량 파일을 처리하는 데 더 비용 효율적일 수 있습니다.
이 기사에서는 CSV 및 XLS/XLSX 파일을 처리하고, 메모리에서 구문 분석하고, 파일을 디스크에 저장하지 않고 구문 분석된 데이터를 반환하는 NestJS의 사용자 정의 파일 업로드 모듈을 만들었습니다. 이 접근 방식은 Node.js 스트림의 기능을 활용하여 서버에 임시 파일이 남지 않으므로 효율적이고 안전합니다.
또한 메모리 내 파일 처리와 디스크에 파일 저장의 장단점을 살펴보았습니다. 인메모리 처리는 속도, 보안 및 단순성을 제공하지만 이 접근 방식을 채택하기 전에 메모리 사용량과 잠재적인 파일 크기 제한을 고려하는 것이 중요합니다.
엔터프라이즈 애플리케이션을 구축하든 소규모 프로젝트를 구축하든 파일 업로드를 올바르게 처리하고 구문 분석하는 것이 중요합니다. 이 설정을 사용하면 불필요한 서버 저장이나 데이터 보안 문제에 대한 걱정 없이 NestJS에서 파일 업로드를 마스터할 수 있습니다.
아래 댓글 섹션에서 여러분의 생각과 개선 사항을 자유롭게 공유해 주세요!
이 기사가 마음에 들었거나 이러한 도구가 유용하다고 생각했다면 Dev.to에서 저를 팔로우하여 코딩 및 개발에 대한 더 많은 통찰력과 팁을 얻으세요. 여러분의 코딩 여정을 더욱 원활하게 만들어줄 유용한 콘텐츠를 정기적으로 공유합니다.
X(트위터)에서 저를 팔로우하세요. 여기서 프로그래밍과 기술에 대한 더 흥미로운 생각, 업데이트, 토론을 공유합니다! 놓치지 마세요 - 팔로우 버튼을 클릭하세요.
또한 LinkedIn에서 저를 팔로우하여 전문적인 통찰력, 최신 프로젝트 업데이트, 코딩, 기술 동향 등에 대한 토론을 받아보실 수도 있습니다. 개발 실력을 한 단계 끌어올릴 수 있는 귀중한 콘텐츠를 놓치지 마세요 - 함께해요!
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!