NestJS 中的轻松文件解析:管理内存中的 CSV 和 XLSX 上传,以提高速度、安全性和可扩展性
在 Web 应用程序中处理文件上传是一项常见任务,但处理不同的文件类型并确保正确处理它们可能具有挑战性。通常,开发人员需要解析上传的文件而不将其保存到服务器,这对于降低服务器存储成本并确保敏感数据不会被不必要地保留尤为重要。在本文中,我们将逐步介绍创建自定义 NestJS 模块来处理专门针对 CSV 和 XLS/XLSX 文件的文件上传的过程,并且我们将使用 Node.js 流在内存中解析这些文件,因此不会有静态文件在服务器上创建。
NestJS 是一个渐进式 Node.js 框架,它利用 TypeScript 并提供开箱即用的应用程序架构,使您能够构建高度可测试、可扩展、松散耦合且易于维护的应用程序。通过使用 NestJS,我们可以利用其模块化结构、强大的依赖注入系统和广泛的生态系统。
在深入研究代码之前,让我们先建立一个新的 NestJS 项目。如果您还没有安装 NestJS CLI:
npm install -g @nestjs/cli
创建一个新的 NestJS 项目:
nest new your-super-name
导航到项目目录:
cd your-super-name
我们需要安装一些额外的软件包来处理文件上传和解析:
npm install @nestjs/platform-express multer exceljsfile-type
为了自定义文件上传过程,我们将创建一个自定义 Multer 存储引擎。该引擎将确保仅接受 CSV 和 XLS/XLSX 文件,使用 Node.js 流在内存中解析它们,并返回解析后的数据,而不将任何文件保存到磁盘。
为我们的引擎创建一个新文件:
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); } }
此自定义存储引擎检查文件的 MIME 类型并确保它是 CSV 或 XLS/XLSX 文件。然后,它使用 Node.js 流完全在内存中处理该文件,因此不会在服务器上创建临时文件。这种方法既高效又安全,尤其是在处理敏感数据时。
解析器工厂负责根据文件类型确定合适的解析器。
为我们的解析器创建一个新文件:
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; }
此工厂函数检查 MIME 类型并返回适当的解析器(xlsx 或 csv)。
接下来,让我们创建一个控制器来使用我们的自定义存储引擎处理文件上传。
生成一个新的控制器:
nest g controller files
在files.controller.ts中,使用Multer和自定义存储引擎配置文件上传:
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); } }
该控制器设置一个端点来处理文件上传。上传的文件由 CsvOrXlsxMulterEngine 处理,解析后的数据在响应中返回,而不会保存到磁盘。
最后,我们需要设置一个模块来包含我们的控制器。
生成一个新模块:
nest g module files
在files.module.ts中,导入控制器:
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 {}
确保将此模块导入到您的 AppModule 中:
为了测试文件上传功能,我们可以创建一个简单的 HTML 页面,允许用户上传 CSV 或 XLS/XLSX 文件。此页面会将文件发送到我们的 /api/files 端点,该端点将在内存中进行解析和处理。
这是用于测试文件上传的基本 HTML 文件:
<!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>
为了渲染文件上传的 HTML 页面,我们首先需要安装一个名为 @nestjs/serve-static 的附加 NestJS 模块。您可以通过运行以下命令来完成此操作:
npm install @nestjs/serve-static
安装后,我们需要在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 {}
此设置将允许我们从公共目录提供静态文件。现在,我们可以通过在浏览器中导航到http://localhost:3000来打开文件上传页面。
上传您的文件
To upload a file, follow these steps:
Once the file is uploaded successfully, you should see a confirmation that the file has been uploaded and formatted.
Note: I haven’t included code for formatting the uploaded file, as this depends on the library you choose for processing CSV or XLS/XLSX files. You can view the complete implementation on GitHub.
Comparing Pros and Cons of In-Memory File Processing
When deciding whether to use in-memory file processing or saving files to disk, it’s important to understand the trade-offs.
No Temporary Files on Disk:
Faster Processing:
Simplified Cleanup:
Memory Usage:
File Size Limitations:
Complexity in Error Handling:
Small to Medium Files: If your application deals with relatively small files, in-memory processing can offer speed and simplicity.
Security-Sensitive Applications: When handling sensitive data that shouldn’t be stored on disk, in-memory processing can reduce the risk of data breaches.
High-Performance Scenarios: Applications that require high throughput and minimal latency may benefit from the reduced overhead of in-memory processing.
Large Files: If your application needs to process very large files, disk-based processing may be necessary to avoid running out of memory.
Resource-Constrained Environments: In cases where server memory is limited, processing files on disk can prevent memory exhaustion and allow for better resource management.
Persistent Storage Needs: If you need to retain a copy of the uploaded file for auditing, backup, or later retrieval, saving files to disk is necessary.
Integration with External Storage Services: For large files, consider uploading them to external storage services like AWS S3, Google Cloud
Scalability: Cloud storage solutions can handle massive files and provide redundancy, ensuring that your data is safe and easily accessible from multiple geographic locations.
Cost Efficiency: Using cloud storage can be more cost-effective for handling large files, as it reduces the need for local server resources and provides pay-as-you-go pricing.
在本文中,我们在 NestJS 中创建了一个自定义文件上传模块,用于处理 CSV 和 XLS/XLSX 文件,在内存中解析它们,并返回解析后的数据,而不将任何文件保存到磁盘。这种方法利用了 Node.js 流的强大功能,使其既高效又安全,因为服务器上不会留下任何临时文件。
我们还探讨了内存中文件处理与将文件保存到磁盘的优缺点。虽然内存处理提供速度、安全性和简单性,但在采用此方法之前考虑内存使用和潜在的文件大小限制非常重要。
无论您是构建企业应用程序还是小型项目,正确处理文件上传和解析都至关重要。通过此设置,您就可以很好地掌握 NestJS 中的文件上传,而无需担心不必要的服务器存储或数据安全问题。
请随时在下面的评论部分分享您的想法和改进!
如果您喜欢本文或发现这些工具很有用,请务必在 Dev.to 上关注我,以获取有关编码和开发的更多见解和技巧。我定期分享有用的内容,让您的编码之旅更加顺利。
在 X (Twitter) 上关注我,我在这里分享更多关于编程和技术的有趣想法、更新和讨论!不要错过 - 点击这些关注按钮。
您还可以在 LinkedIn 上关注我,获取专业见解、最新项目的更新以及有关编码、技术趋势等的讨论。不要错过可以帮助您提高开发技能的有价值的内容 - 让我们联系!
以上是简化 NestJS 中的文件上传:无需磁盘存储即可高效内存解析 CSV 和 XLSX的详细内容。更多信息请关注PHP中文网其他相关文章!