1

有没有人有如何使用 GraphQl 在 NestJs 中上传文件的示例?

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

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

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

4

7 回答 7

4

Apollo Server 2.0 现在应该能够做到这一点(打包在嵌套中),虽然我需要安装graphql-upload和导入GraphQLUpload,因为我找不到Upload类型:

@Mutation(() => Image, { nullable: true })
async addImage(@Args({ name: 'image', type: () => GraphQLUpload }) image) {
  // Do stuff with image...
}
于 2020-02-03T16:03:10.910 回答
2

在给出这个答案的时候,FileInterceptor正在使用multer并通过转换ExecutionContexthttp它来提供的用途getRequestgetResponse方法,以及req它们在GraphQL 中是(和)未定义的。resmulter.singlereqres

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

const ctx = GqlExecutionContext.create(context);

并且req有财产,ctx但我(还)找不到使用方法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
    ...
于 2018-10-18T11:57:45.780 回答
1

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

  1. 包.json

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

  1. 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")
  }
}
  1. 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

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

于 2021-05-20T14:42:04.743 回答
0

编辑:根据下面的Developia 评论,apollo-server 现在实现了文件上传。应该是首选方式。

下面,原答案,供参考。

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

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

mutation Mutation {
  uploadFile(base64: String): Int
}

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

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

于 2018-04-23T06:24:25.960 回答
0

试试这个

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))
        );
    }
    
}
于 2020-08-25T11:24:02.993 回答
-2

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

于 2018-07-24T09:57:21.743 回答
-4

您需要定义一个上传控制器并将其添加到您的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));
于 2019-03-05T13:34:25.597 回答