NestJS 在构建高效且可扩展的 Node.js 服务器端应用程序方面别具优势,越来越多的团队在大型 Node.js 服务端项目中使用 NestJS,非常值得前端学习。
本文主要记录一下对一个小功能的实现,也是日常开发比较实用的功能,就是通过Nest来实现一个接口,读取客户端上传的文件保存到本地,并且解析excel将文件中的每一列存入到数据库中
接口Controller层实现
js
@Controller('stockx')
@UseGuards(JwtGuard, RoleGuard)
export class StockxController {
constructor(private readonly stockxService: StockxService) {}
@Post('updateSPGFromExcel')
@UseInterceptors(FileInterceptor('file'))
updateSPGFromExcel(@UploadedFile() file: Express.Multer.File) {
return this.stockxService.updateSPGFromExcel(file);
}
-
Controller 装饰器 :
@Controller('stockx')
: 这个装饰器用于在/stockx
下设置控制器的基本路由。 -
Guard 装饰器 :
@UseGuards(JwtGuard, RoleGuard)
: 这个装饰器将守卫应用于控制器,以基于 JWT 认证 (JwtGuard
) 和角色授权 (RoleGuard
) 限制访问。 -
构造函数 : 构造函数注入了一个
StockxService
的实例,这个服务负责处理与 StockX 服务相关的业务逻辑。我们直接在函数中调用service的updateSPGFromExcel
这里的@UploadedFile()
是 NestJS 的装饰器,用于从 HTTP 请求中提取上传的文件。它告诉 NestJS 在处理 HTTP 请求时将上传的文件绑定到被装饰的参数上。在这个特定的情况下,file
参数被装饰为 Express.Multer.File
类型,这是 Multer 中间件提供的类型,表示上传的文件对象的类型。
在postman中这样去调用请求即可
接口Service层实现
由于需要先将文件保存到本地,接着对文件的数据进行解析,先完成文件保存,一个文件可能被上传多次,这里使用了uuid来给每个文件命名成唯一的名字,防止文件名冲突,通过fs来讲
js
async updateSPGFromExcel(file) {
try {
// 将上传的文件保存到本地
const uuid = uuidv4()
let filename = `${uuid}_spg.xlsx`
const filePath = path.join('/home/www/nestjs-admin/uploads',filename);
const writeStream = fs.createWriteStream(filePath);
// // 将上传的文件写入到可写流中
writeStream.write(file.buffer);
return { message: 'File uploaded and processed successfully' };
} catch (err) {
console.error('Error processing file:', err);
return { message: 'Error processing file' };
}
//
}
创建了一个写入流 (writeStream
),它将数据写入到指定的文件路径中,传入要写入的文件的路径作为参数,这里使用path.join来拼接一下路径即可
解析传入的excel
这里可以多学习一个知识点就是解析excel,所以我把这两步分开写了,先保存文件,在对保存好的文件进行解析
有个需要注意的地方就是这里是一个函数中执行两步操作
第一个是保存文件
第二个是解析保存的文件
那么就会出现一种情况就是文件还没保存好,就已经开始解析,所以这种情况下要利用end操作,准确的知道写入文件完成的时候,再去执行解析操作,并且在finish中去执行解析。
writeStream.end()
是用于结束写入流的方法。在 Node.js 中,当使用可写流时,必须调用.end()
方法来明确地标记写入操作的结束。这样做有几个目的:
- 确保所有数据都已写入 :调用
.end()
方法会将所有数据写入到流中,然后关闭流,这样可以确保所有数据都已成功写入到文件中。- 触发 'finish' 事件 :当写入操作完成时,可写流会触发 'finish' 事件。通过调用
.end()
方法,可以确保在数据全部写入后触发这个事件,从而执行相应的后续操作。
js
// 明确结束写入操作,以确保触发 'finish' 事件
writeStream.end();
// 监听可写流的 'finish' 事件,表示写入操作完成
writeStream.on('finish',async () => {
console.log('Write operation finished.');
// 执行其他处理,比如解析 Excel 文件等
const workbook = xlsx.readFile(filePath);
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
const excelData = xlsx.utils.sheet_to_json(worksheet, { header: 1 });
let data: any = excelData.slice(1)
console.log(data,'dadadadada')
}catch(e){
console.log('errr',e)
}
});
上面的代码的一些解析
-
const workbook = xlsx.readFile(filePath);
:- 使用
xlsx
库的readFile
方法读取指定路径下的 Excel 文件。
- 使用
-
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
:- excel表格可能存在多个工作表,SheetNames【0】读取的是第一个工作表
-
const excelData = xlsx.utils.sheet_to_json(worksheet, { header: 1 });
:- 将数据转换成json格式,并且使用
{ header: 1 }
配置,表示将工作表中的第一行作为 JSON 对象的属性名。
- 将数据转换成json格式,并且使用
-
let data: any = excelData.slice(1);
:- 将 Excel 数据存储在变量
data
中,并排除了第一行标题行。
- 将 Excel 数据存储在变量
存入数据库
js
try{
let spg = []
for (const item of data) {
let obj = {
spgId:'12312312',//唯一id编号
spid:item[1],
sname:item[2],
surl:item[3],
}
spg.push(obj)
}
console.log(spg,'spg')
await this.spgRepo.save(spg);
生成了data数据后可以通过下标的方式来访问data中拿到的数据,比如0下标则是表格的第一列
由于excel的数据量可能会比较多,所以对解析后的data循环并且做好数据格式放到数组中,保存成一个数据,再通过save
的格式存到对应的表中