如何在NestJS与TypeScript中使用File作为数据类型?
解决NestJS GraphQL中无法声明File类型的问题
在NestJS GraphQL场景下,DTO中无法直接使用原生File类型会触发Undefined type error,以下是几种可行的解决方案:
方案一:使用GraphQL Upload scalar处理文件
这是GraphQL官方推荐的文件上传方案,需要配置对应的标量类型:
- 安装依赖
确保安装兼容版本的包:
npm install graphql-upload @nestjs/graphql graphql
(注:NestJS 10+建议搭配graphql-upload@^16.0.2使用)
- 注册Upload标量
在GraphQL模块中注册Upload标量类型:
import { Module } from '@nestjs/common'; import { GraphQLModule } from '@nestjs/graphql'; import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo'; import { GraphQLUpload } from 'graphql-upload'; @Module({ imports: [ GraphQLModule.forRoot<ApolloDriverConfig>({ driver: ApolloDriver, autoSchemaFile: true, resolvers: { Upload: GraphQLUpload }, // 注册Upload标量 }), ], }) export class AppModule {}
- 在DTO中使用FileUpload类型
创建输入DTO并显式指定Upload类型:
import { InputType, Field } from '@nestjs/graphql'; import { FileUpload } from 'graphql-upload'; @InputType() export class CreateItemInput { @Field(() => GraphQLUpload) image: FileUpload; // 其他业务字段... }
- 在Resolver中处理文件
接收并处理上传的文件流:
import { Resolver, Mutation, Args } from '@nestjs/graphql'; import { FileUpload, createWriteStream } from 'graphql-upload'; import { promisify } from 'util'; import { pipeline } from 'stream'; const pump = promisify(pipeline); @Resolver() export class ItemResolver { @Mutation(() => Boolean) async uploadImage(@Args('input') input: CreateItemInput) { const { createReadStream, filename } = await input.image; // 将文件保存到本地或云存储 await pump(createReadStream(), createWriteStream(`./uploads/${filename}`)); return true; } }
方案二:通过Base64编码传递文件
如果不想配置GraphQL Upload,可以将文件转为Base64字符串传递,后端用字符串类型接收:
- 前端转换文件为Base64
在React端将File对象转为Base64字符串:
const fileToBase64 = (file) => { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(file); }); }; // 调用示例 const base64Image = await fileToBase64(selectedFile);
- 后端DTO接收字符串类型
import { InputType, Field } from '@nestjs/graphql'; @InputType() export class CreateItemInput { @Field() image: string; // 接收Base64格式的字符串 // 其他业务字段... }
- 后端解析Base64为文件
在Resolver中将Base64字符串转为文件:
import { Resolver, Mutation, Args } from '@nestjs/graphql'; import { writeFile } from 'fs/promises'; @Resolver() export class ItemResolver { @Mutation(() => Boolean) async uploadImage(@Args('input') input: CreateItemInput) { // 移除Base64前缀(如data:image/png;base64,) const base64Data = input.image.replace(/^data:image\/\w+;base64,/, ''); const buffer = Buffer.from(base64Data, 'base64'); await writeFile('./uploads/image.png', buffer); return true; } }
方案三:改用REST接口处理文件上传
如果GraphQL不是必须的,直接用NestJS的Multer中间件处理REST接口的文件上传:
- 配置Multer模块
import { Module } from '@nestjs/common'; import { MulterModule } from '@nestjs/platform-express'; import { diskStorage } from 'multer'; import { extname } from 'path'; @Module({ imports: [ MulterModule.register({ storage: diskStorage({ destination: './uploads', filename: (req, file, cb) => { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9); cb(null, `${uniqueSuffix}${extname(file.originalname)}`); }, }), }), ], }) export class AppModule {}
- 创建REST上传接口
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; @Controller('upload') export class UploadController { @Post() @UseInterceptors(FileInterceptor('image')) async uploadFile(@UploadedFile() file: Express.Multer.File) { // 返回文件访问路径或URL return { url: `/uploads/${file.filename}` }; } }
前端调用该REST接口获取文件URL后,再将URL传入GraphQL接口使用即可。
内容的提问来源于stack exchange,提问作者Jawad Ali
相关产品推荐
相关产品推荐

