首页 > 解决方案 > 如何使用 NestJS graphql 上传多个文件?

问题描述

我想创建一个突变,向服务器发送一个包含字符串字段的对象和一个包含照片和描述数组的字段。这是我的输入类型:

import { InputType, Field } from '@nestjs/graphql';
import {
  IsString,
  MinLength,
  ValidateNested,
  IsOptional,
  IsBoolean,
  IsUUID,
  IsNotEmptyObject,
} from 'class-validator';
import { FileUpload } from 'graphql-upload';
import { GraphQLUpload } from 'apollo-server-express';

@InputType()
export class HouseTourInput {
  @Field()
  @IsString()
  @MinLength(2)
  title: string;

  @Field()
  @IsString()
  @MinLength(2)
  @IsOptional()
  summary: string;

  @Field()
  @IsString()
  @MinLength(2)
  @IsOptional()
  category: string;

  @Field()
  @IsBoolean()
  @IsOptional()
  featured: boolean;

  @Field()
  @IsUUID()
  authorId: string;

  @Field(type => [SlidesInput])
  @ValidateNested({ each: true })
  slides: SlidesInput[];
}

@InputType()
class SlidesInput {
  @Field(() => GraphQLUpload)
  @IsNotEmptyObject()
  photo: FileUpload;

  @Field()
  @IsString()
  @MinLength(2)
  description: string;
}

这是我的解析器:

@Resolver(of => HouseTour)
export class AdminResolver {
  constructor(private adminService: AdminService) {}

  @Mutation(returns => HouseTour)
  async createHouseTour(
    @Context('user') user: IMe,
    @Args('input') input: HouseTourInput,
  ) {
    console.log(input);

    // More code irrelevant to this question
  }

输入永远不会记录在控制台中,因为代码似乎永远不会超出验证点。这是错误日志:

  TypeError: Promise resolver undefined is not a function
    at new Promise (<anonymous>)
    at TransformOperationExecutor.transform (/Users/babatundeadeyemi/workspace/234homesbackend/node_modules/class-transformer/TransformOperationExecutor.js:139:32)
    at TransformOperationExecutor.transform (/Users/babatundeadeyemi/workspace/234homesbackend/node_modules/class-transformer/TransformOperationExecutor.js:273:43)
    at /Users/babatundeadeyemi/workspace/234homesbackend/node_modules/class-transformer/TransformOperationExecutor.js:73:40
    at Array.forEach (<anonymous>)
    at TransformOperationExecutor.transform (/Users/babatundeadeyemi/workspace/234homesbackend/node_modules/class-transformer/TransformOperationExecutor.js:45:19)
    at TransformOperationExecutor.transform (/Users/babatundeadeyemi/workspace/234homesbackend/node_modules/class-transformer/TransformOperationExecutor.js:273:43)
    at ClassTransformer.plainToClass (/Users/babatundeadeyemi/workspace/234homesbackend/node_modules/class-transformer/ClassTransformer.js:17:25)
    at Object.plainToClass (/Users/babatundeadeyemi/workspace/234homesbackend/node_modules/class-transformer/index.js:29:29)
    at ValidationPipe.transform (/Users/babatundeadeyemi/workspace/234homesbackend/node_modules/@nestjs/common/pipes/validation.pipe.js:42:39)

我不知道还能做什么。我用谷歌搜索并没有找到答案。

标签: typescriptgraphqlnestjs

解决方案


我不确定您的错误的根本原因是什么,但我自己偶然发现了 graphql 上传问题,这令人沮丧。

幸运的是,我遇到了以下 github 问题,它为我提供了一个可行的解决方案。需要禁用apollo内部graphql的上传,使用graphql-upload

这是我在 NestJS GraphQL 上上传多张图片的最终解决方案。确保在项目的根目录中有一个上传文件夹。

src/app.module.ts

import { GraphQLUpload, graphqlUploadExpress } from "graphql-upload"
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common"

@Module({
  imports: [
    GraphQLModule.forRoot({
      resolvers: { Upload: GraphQLUpload }, // define the Upload Scalar
      typePaths: ['./**/*.graphql'],
      uploads: false, // disable built-in upload handling
    }),
  ],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(graphqlUploadExpress()).forRoutes("graphql")
  }
}

src/app.resolver.ts

import { Args, Mutation, Query,  Resolver} from '@nestjs/graphql';
import { UseGuards, Inject, Post } from '@nestjs/common';
import { createWriteStream } from 'fs';
import { FileUpload, GraphQLUpload } from "graphql-upload";

@Resolver()
export class AppResolvers {
  @Mutation('uploadImages')
  async uploadFile(@Args('images', {type: () => GraphQLUpload}) imgs: Promise<FileUpload>[]):  Promise<Promise<string>[]> {
    return await Promise.all(
      imgs.map(async (img: Promise<FileUpload>) : Promise<Promise<string>>=>{
        const { filename, mimetype, encoding, createReadStream } = await img;
        console.log("attachment:", filename, mimetype, encoding)
        const stream = createReadStream();
        return new Promise((resolve,reject) =>{
          stream.on('end', () => {console.log("ReadStream Ended")})
            .on('close', () => {console.log("ReadStream Closed")})
            .on('error', (err) => {console.error("ReadStream Error",err)})
          .pipe(createWriteStream(`./upload/${filename}`))
            .on('end', () => {
              console.log("WriteStream Ended");
              resolve("end")
            })
            .on('close', () => {
              console.log("WriteStream Closed");
              resolve("close")
            })
            .on('error',(err) => {
              console.log("WriteStream Error",err);
              reject("error")
            });
        });
      })
    )
  }
}

src/app.graphql

scalar Upload

type Mutation {
    uploadImages(
        images: [Upload]
    ): [String]
}

推荐阅读