NestJS 使用 GraphQL 上传 [关闭]

Posted

技术标签:

【中文标题】NestJS 使用 GraphQL 上传 [关闭]【英文标题】:NestJS upload using GraphQL [closed] 【发布时间】:2018-08-08 14:30:03 【问题描述】:

有没有人举例说明如何使用 GraphQl 在 NestJs 中上传文件?

我可以通过控制器使用给定的示例上传

https://github.com/nestjs/nest/issues/262#issuecomment-366098589,

但我找不到任何全面的文档如何在 NestJS 中使用 GrahpQL 进行上传

【问题讨论】:

【参考方案1】:

Apollo Server 2.0 现在应该可以做到这一点(封装在 Nest 中),虽然我需要安装 graphql-upload 并导入 GraphQLUpload,因为我找不到 Upload 类型:

@Mutation(() => Image,  nullable: true )
async addImage(@Args( name: 'image', type: () => GraphQLUpload ) image) 
  // Do stuff with image...

【讨论】:

【参考方案2】:

在回答此问题时,FileInterceptor 正在使用 multer 并通过将 ExecutionContext 转换为 http 它使用 getRequestgetResponse 方法来提供 reqresmulter.single它们在 GraphQL 中未定义(reqres)。

我尝试使用以下方法从上下文中获取请求:

const ctx = GqlExecutionContext.create(context);

ctx 中有 req 属性,但我找不到使用 multer 的方法(还)。

无论如何,我对FileFieldsInterceptor 进行了一些更改以在我的项目中使用它,但是当我有时间清理它时,我可能会提出拉取请求:

import  Observable  from 'rxjs';
import 
  NestInterceptor,
  Optional,
  ExecutionContext,
  mixin,
 from '@nestjs/common';
import  GqlExecutionContext  from '@nestjs/graphql';
import  storeFile  from './storeFile';

interface IField 
  name: string;
  options?: any;


export function GraphqlFileFieldsInterceptor(
  uploadFields: IField[],
  localOptions?: any,
) 
  class MixinInterceptor implements NestInterceptor 
    options: any = ;
    constructor(@Optional() options: any = ) 
      this.options =  ...options, ...localOptions ;
    

    async intercept(
      context: ExecutionContext,
      call$: Observable<any>,
    ): Promise<Observable<any>> 
      const ctx = GqlExecutionContext.create(context);
      const args = ctx.getArgs();

      let storeFilesResult = await Promise.all(
        uploadFields.map(uploadField => 
          const file = args[uploadField.name];
          return storeFile(file, 
            ...uploadField.options,
            ...this.options,
          ).then(address => 
            args[uploadField.name] = address;
            return address;
          );
        ),
      );

      return call$;
    
  
  const Interceptor = mixin(MixinInterceptor);
  return Interceptor;

和存储文件是这样的(可能不会这样使用):

import uuid from 'uuid/v4';
import fs from 'fs';
import path from 'path';

const dir = './files';
if (!fs.existsSync(dir)) 
  fs.mkdirSync(dir);


export const storeFile = async (file, options): Promise<any> => 
  // options is not doing anything right now
  const  stream  = await file;
  const filename = uuid();

  const fileAddress = path.join(dir, filename + '.jpg');
  return new Promise((resolve, reject) =>
    stream
      .on('error', error => 
        if (stream.truncated)
          // Delete the truncated file
          fs.unlinkSync(fileAddress);
        reject(error);
      )
      .pipe(fs.createWriteStream(fileAddress))
      .on('error', error => reject(error))
      .on('finish', () => resolve(fileAddress)),
  );
;

在我的Cats.resolvers.ts

...
  @Mutation()
  @UseInterceptors(
    GraphqlFileFieldsInterceptor([
       name: 'catImage1' ,
       name: 'catImage2' ,
       name: 'catImage3' ,
    ]),
  )
  async cats(
    @Args('catImage1') catImage1: string,
    @Args('catImage2') catImage2: string,
    @Args('catImage3') catImage3: string,
  )
    console.log(catImage1) // will print catImage1 address
    ...

【讨论】:

【参考方案3】:

此实现与 Node >= v14 完美配合

    package.json

如果添加了 fs-capacitor 和 graphql-upload 条目,请从解决方案部分删除它们,并安装最新版本的 graphql-upload(此时为 v11.0.0)包作为依赖项。

    src/app.module.ts

禁用 Apollo Server 的内置上传处理并将 graphqlUploadExpress 中间件添加到您的应用程序。

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

@Module(
  imports: [
    GraphQLModule.forRoot(
      uploads: false, // disable built-in upload handling
    ),
  ],
)
export class AppModule implements NestModule 
  configure(consumer: MiddlewareConsumer) 
    consumer.apply(graphqlUploadExpress()).forRoutes("graphql")
  

    src/blog/post.resolver.ts(示例解析器)

从 apollo-server-core 中移除 GraphQLUpload 导入,改为从 graphql-upload 导入

import  FileUpload, GraphQLUpload  from "graphql-upload"

@Mutation(() => Post)
async postCreate(
  @Args("title") title: string,
  @Args("body") body: string,
  @Args("attachment",  type: () => GraphQLUpload ) attachment: Promise<FileUpload>,
) 
  const  filename, mimetype, encoding, createReadStream  = await attachment
  console.log("attachment:", filename, mimetype, encoding)

  const stream = createReadStream()
  stream.on("data", (chunk: Buffer) => /* do stuff with data here */)

来源: https://github.com/nestjs/graphql/issues/901#issuecomment-780007582

我发现其他一些有用的链接:

https://stephen-knutter.github.io/2020-02-07-nestjs-graphql-file-upload/ 用于使用邮递员上传文件Link

【讨论】:

【参考方案4】:

编辑:根据下面的Developia comment,apollo-server 现在实现了file upload。应该是首选方式。

以下,原答案,供参考。

通常不使用 GraphQL 进行上传。 GraphQL 是花哨的“API 规范”,这意味着最终,低级别的 HTTP 请求和响应会被转换为 JSON 对象或从 JSON 对象转换(如果您没有自定义传输)。

一种解决方案可能是在 GraphQL 架构中定义特殊端点,例如:

mutation Mutation 
  uploadFile(base64: String): Int

然后客户端会将二进制数据转换为 base64 字符串,这将在解析器端进行相应处理。这样,文件将成为 GraphQL 客户端和服务器之间交换的 JSON 对象的一部分。

虽然这可能适合小文件,少量操作,但绝对不是上传服务的解决方案。

【讨论】:

如果您要上传多个文件或文件很大,则使用 Base64 上传文件是不可扩展的。现在可以使用当前合并到 apollo-server@2 中的 apollo-upload-server,因此它可以处理多部分表单数据。 Nestjs 提供 apollo-graphql@2 作为 gql 服务器,OP 要求提供文档,因为尽管(逻辑上)可能在 nestjs 文档中没有样本。【参考方案5】:

试试这个

import  Resolver, Mutation, Args  from '@nestjs/graphql';
import  createWriteStream  from 'fs';

import GraphQLUpload from "apollo-server-express"

@Resolver('Download')
export class DownloadResolver 
    @Mutation(() => Boolean)
    async uploadFile(@Args(name: 'file', type: () => GraphQLUpload)
    
        createReadStream,
        filename
    ): Promise<boolean> 
        return new Promise(async (resolve, reject) => 
            createReadStream()
                .pipe(createWriteStream(`./uploads/$filename`))
                .on('finish', () => resolve(true))
                .on('error', () => reject(false))
        );
    
    

【讨论】:

【参考方案6】:

您可以使用 apollo-upload-server 库。在我看来,这似乎是最简单的事情。干杯

【讨论】:

这不是 OP 要求的答案。【参考方案7】:

您需要定义一个上传控制器并将其添加到您的 app.module 中,这是一个控制器应该是(后端)的示例:

@Controller()
export class Uploader 
  @Post('sampleName')
  @UseInterceptors(FileInterceptor('file'))
  uploadFile(@UploadedFile() file) 
  // file name selection 
    const path = `desired path`;
    const writeStream = fs.createWriteStream(path);  
    writeStream.write(file.buffer);
    writeStream.end();
    return 
      result: [res],
    ;
  

并在前端通过 fetch 调用您的控制器:

    fetch('controller address', 
          method: 'POST',
          body: data,
        )
          .then((response) => response.json())
          .then((success) => 
            // What to do when succeed 
);
          )
          .catch((error) => console.log('Error in uploading file: ', error));

【讨论】:

这个答案没有使用 GraphQL。

以上是关于NestJS 使用 GraphQL 上传 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 nestjs-graphql-fastify 服务器上传文件以及如何测试此类功能?

Nestjs graphql 上传错误:缺少 POST 正文。你忘记使用 body-parser 中间件了吗?

Nestjs - @nestjs/graphql GraphQLGatewayModule 总是返回错误请求

错误:使用 Nestjs + Graphql + Typeorm 时无法确定 GraphQL 输入类型

NestJS:使用 graphql 的 Passport LinkedIn 策略

GraphQL介绍&使用nestjs构建GraphQL查询服务