Как мне загрузить несколько файлов с помощью NestJS graphql?

#typescript #graphql #nestjs

#typescript #graphql #nestjs

Вопрос:

Я хочу создать мутацию, которая отправляет на сервер объект, содержащий строковые поля, и поле, содержащее массив фотографий и описаний. Вот мой inputType:

 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)
  

Я не знаю, что еще делать. Я погуглил и не нашел ответа.

Комментарии:

1. Проверка graphql-проблема с загрузкой нескольких файлов, которая начинается github.com/jaydenseric/graphql-upload/issues/202

2. Я уже видел эту проблему. Это не решает мою проблему. Вместо этого мне пришлось создать конечную точку REST API только для этой функции.

3. Возникла та же проблема. Это class-validator связано с тем, что внутренняя попытка преобразовать объект (поскольку экземпляр файла является объектом) завершается неудачей. Мне пришлось добавить @Exclude декоратор в поле file, но вы также можете изменить ValidationPipe параметры.

Ответ №1:

Я не уверен, в чем основная причина вашей ошибки, но я сам столкнулся с проблемой загрузки graphql, и это расстраивало.

К счастью, я столкнулся со следующей проблемой github, которая предоставила мне рабочее решение. Необходимо отключить загрузку внутреннего graphql в apollo и использовать 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]
}