🚀Nest.js实现Excel上传并解析数据存入数据库

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);
    }
  
  1. Controller 装饰器 : @Controller('stockx'): 这个装饰器用于在 /stockx 下设置控制器的基本路由。

  2. Guard 装饰器 : @UseGuards(JwtGuard, RoleGuard): 这个装饰器将守卫应用于控制器,以基于 JWT 认证 (JwtGuard) 和角色授权 (RoleGuard) 限制访问。

  3. 构造函数 : 构造函数注入了一个 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() 方法来明确地标记写入操作的结束。这样做有几个目的:

  1. 确保所有数据都已写入 :调用 .end() 方法会将所有数据写入到流中,然后关闭流,这样可以确保所有数据都已成功写入到文件中。
  2. 触发 '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)
            }
        });

上面的代码的一些解析

  1. const workbook = xlsx.readFile(filePath);

    • 使用 xlsx 库的 readFile 方法读取指定路径下的 Excel 文件。
  2. const worksheet = workbook.Sheets[workbook.SheetNames[0]];

    • excel表格可能存在多个工作表,SheetNames【0】读取的是第一个工作表
  3. const excelData = xlsx.utils.sheet_to_json(worksheet, { header: 1 });

    • 将数据转换成json格式,并且使用 { header: 1 } 配置,表示将工作表中的第一行作为 JSON 对象的属性名。
  4. let data: any = excelData.slice(1);

    • 将 Excel 数据存储在变量 data 中,并排除了第一行标题行。

存入数据库

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的格式存到对应的表中

如果觉得有趣或有收获,请关注我的更新,给个喜欢和分享。您的支持是我写作的最大动力!

往期好文推荐

相关推荐
霍先生的虚拟宇宙网络16 分钟前
webp 网页如何录屏?
开发语言·前端·javascript
温吞-ing18 分钟前
第十章JavaScript的应用
开发语言·javascript·ecmascript
彪82519 分钟前
第十章 JavaScript的应用 习题
javascript·css·ecmascript·html5
jessezappy36 分钟前
jQuery-Word-Export 使用记录及完整修正文件下载 jquery.wordexport.js
前端·word·jquery·filesaver·word-export
Rverdoser44 分钟前
RabbitMQ的基本概念和入门
开发语言·后端·ruby
旧林8431 小时前
第八章 利用CSS制作导航菜单
前端·css
yngsqq1 小时前
c#使用高版本8.0步骤
java·前端·c#
Tech Synapse2 小时前
Java根据前端返回的字段名进行查询数据的方法
java·开发语言·后端
.生产的驴2 小时前
SpringCloud OpenFeign用户转发在请求头中添加用户信息 微服务内部调用
spring boot·后端·spring·spring cloud·微服务·架构
微信-since811922 小时前
[ruby on rails] 安装docker
后端·docker·ruby on rails