如何使用NestJS和MySQL建立一个文件上传功能

660 阅读6分钟

用NestJS和MySQL处理文件上传

许多开发者都鄙视处理文件上传。在这篇博客中,我们将教你如何使用NestJS和MySQL建立一个文件上传功能。

许多开发者都不喜欢处理文件上传的问题。这可能是由于缺乏关于采取最佳方法的知识,或者难以确定如何配置他们的NestJS应用程序来处理文件上传。许多人可能想把文件直接保存到MySQL数据库中,或保存图像名称并让图像保存在磁盘存储中:这一切都取决于他们的喜好和他们想要实现的目标。本教程将教你如何使用NestJS和MySQL建立一个文件上传功能。

前提条件

在你开始学习本教程之前,请确保你的系统满足以下要求。

设置NestJS

一旦满足上述要求,继续安装NestJS CLI,并通过运行以下命令创建一个新项目。

Shell

$ npm i -g @nestjs/cli $ nest new file-upload

这些命令将安装NestJS CLI并创建一个新的NestJS项目,其文件夹结构如下。

在NestJS项目创建完成后,继续下一步--通过运行以下命令为你的应用程序安装所需的依赖。

Shell

npm install --save @nestjs/typeorm typeorm mysql2 

在上述命令中,你已经安装了TypeORMmysql2模块:它们将使你的应用程序连接到MySQL数据库并对其进行操作。

设置MySQL数据库

在安装了上述依赖项后,继续设置并连接到你的MySQL数据库。要开始,在app.module.ts 文件中添加下面的代码片段:

TypeScript

...
import { TypeOrmModule } from '@nestjs/typeorm';
import { Image } from './image.entity';

@Module({
  imports: [TypeOrmModule.forRoot({
    type: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'root',
    password: '1234',
    database: 'blog',
    entities: [Image],
    synchronize: true,
  }),
  TypeOrmModule.forFeature([Image])
  ],
  ...
})
...

在上面的代码片断中,我们从之前安装的typeorm模块中导入了TypeOrmModule 。我们使用forRoot 方法将应用程序连接到MySQL数据库并传入数据库凭证。这里需要指出的另一件事是entities 属性,它允许我们在我们的模块中指定实体,它将使我们能够访问你即将创建的Image 实体:我们还将synchronize 属性设置为true ,以自动迁移数据库。

创建图像实体

接下来,让我们来创建我们前面提到的Image实体。为了开始,在src目录下创建一个image.entity.ts文件,并添加下面的代码片段。

TypeScript

import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';

@Entity()
export class Image {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @CreateDateColumn()
    dateCreated: Date;

    @UpdateDateColumn()
    dateUpdated: Date;
}

在上面的代码片段中,我们导入了创建实体所需的装饰器。使用这些装饰器,我们定义了实体的属性。我们有id 字段,使用@PrimaryGeneratedColumn() 装饰器为数据库中的每条记录生成随机ID;name 字段,使用@Column 装饰器存储将被上传的图片名称;dateCreated 和 dateUpdate 字段,使用@CreateDateColumn()@UpdateDateColumn() 保存记录的创建和更新日期。

创建上传服务

随着Image实体的创建,让我们创建一个服务来执行CRUD操作以处理文件上传。在app.service.ts 文件中,添加下面的代码片段。

TypeScript

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Image } from './image.entity';

@Injectable()
export class AppService {
  constructor(
    @InjectRepository(Image)
    private readonly imageRepository: Repository<Image>,
  ) {}

  async getImages(): Promise<Image[]> {
    return this.imageRepository.find();
  }

  async createImage(image: Image): Promise<Image> {
    return this.imageRepository.save(image);
  }

  async getImage(id: number): Promise<Image> {
    return this.imageRepository.findOneBy({ id });
  }

  async deleteImage(id: number): Promise<void> {
    await this.imageRepository.delete(id);
  }
}

在上面的代码片段中,我们已经导入了injectRepository 装饰器,将imageRepository 注入到AppServiceRepository ,这为你提供了在数据库中执行一些操作所需的方法。因此,对于createImage 图像服务,我们正在保存被上传的图像的名称,这将通过控制器传递。

创建上传控制器

现在让我们创建控制器来使用这些服务。在app.controller.ts 文件中,添加下面的代码片段。

TypeScript

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Image } from './image.entity';

@Injectable()
export class AppService {
  constructor(
    @InjectRepository(Image)
    private readonly imageRepository: Repository<Image>,
  ) {}

  async getImages(): Promise<Image[]> {
    return this.imageRepository.find();
  }

  async createImage(image: Image): Promise<Image> {
    return this.imageRepository.save(image);
  }

  async getImage(id: number): Promise<Image> {
    return this.imageRepository.findOneBy({ id });
  }

  async deleteImage(id: number): Promise<void> {
    await this.imageRepository.delete(id);
  }
}

在上面的代码片段中,我们导入了几个装饰器,如FileInterceptorUploadedFile 、和UseInterceptorsFileInterceptor() 拦截器到路由处理程序,使用@UploadedFile() 装饰器从请求中提取文件。FileInterceptor() 装饰器是从@nestjs/platform-express 包中导出的。@UploadedFile() 装饰器是从@nestjs/common 输出的。FileInterceptor() 装饰器需要两个参数,fieldName ,它是提供HTML表单中存放文件的字段名称的字符串,以及options ,它是一个MulterOptions类型的可选对象。这是multer构造函数所使用的同一个对象。

multer关于createImage 函数,我们使用上述装饰器来处理文件上传,使用FileInterceptor() 传递图像的字段名,我们修改了FileInterceptor() 函数,通过使用diskStorage 函数指定storage 属性将图像上传到磁盘。然后我们指定了图像的位置,并为图像生成了随机名称。此外,我们还添加了一个filter 属性来限制某些图像格式的上传。现在,我们使用@UploadedFile() 装饰器得到提取的文件,并得到名称,然后将其保存到数据库中。这样,我们就可以使用每张图片的名称从存储位置获取图片。

为了使上述代码工作,你需要在终端运行以下命令来安装multer。

Shell

npm i -D @types/multer 

然后,你需要在app.module.ts 文件的导入阵列中注册multer模块。

TypeScript

...
import { MulterModule } from '@nestjs/platform-express';


@Module({
  ...
  MulterModule.register({
    dest: './files',
  }),],
  ...

上述配置告诉multer处理文件的上传,以及上传文件的位置。最后但并非最不重要的是,我们应该在src 目录中创建一个files 文件夹来实际存储文件。

服务文件

为了真正地将上传的图片提供给用户,你需要通过运行下面的命令安装serve-static 模块。

外壳

npm install --save @nestjs/serve-static 

然后,用下面的代码片断在app.module.ts 文件的导入数组中注册ServeStaticModule

TypeScript

...
import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

@Module({
  ...
  ServeStaticModule.forRoot({
    rootPath: join(__dirname, '..', 'files')
  }),],
  ...

在上面的代码片段中,你已经指定了文件所在的位置,并且可以从那里获得服务。

测试API

现在打开Postman,通过向端点localhost:4000/images 发送POST请求来测试该应用程序,并在请求体中以form-data的形式传递有效载荷。 如果你现在看一下文件文件夹,你应该看到你上传的文件。请随意:也可以测试和玩弄其他的路由。

总结

通过本教程,你已经学会了如何用NestJS和MySQL处理文件上传。你已经学会了如何使用TypeORM连接到MySQL数据库,你还创建了一个实体并将图片上传到NestJS应用程序中。