【问题标题】:How to upload images to fastify + graphql backend with axios?如何使用 axios 将图片上传到 fastify + graphql 后端?
【发布时间】:2020-09-01 17:45:17
【问题描述】:

当通过 axios 发送图像时,我发现我必须使用 formdata。我在这里添加了我的图片,但是在发送表单数据时,我的整个后端都冻结了,只是说“待处理”。

我一直在关注this

到目前为止我的尝试:

后端:

阿波罗:

import { ApolloServer, makeExecutableSchema } from 'apollo-server-fastify';

const schema = makeExecutableSchema({ typeDefs, resolvers });

const apolloServer = new ApolloServer({
  schema,
  uploads: {
    maxFileSize: 10000000,
    maxFiles: 5,
  },
});

(async function() {
  app.register(apolloServer.createHandler({ path: '/api' }));
})();

架构:

  scalar DateTime
  scalar Upload

  input addUser {
    Email: String!
    Password: String
    FirstName: String!
    LastName: String!
    Age: DateTime!
    JobTitle: String!
    File: Upload
  }

  type Mutation {
    register(input: addUser!): Boolean
  }

解析器:

  Mutation: {
    register: async (obj, args, context, info) => {
        // how to get the formData?
      },
  }

前端:

我这样构建请求:

const getMutation = (mutate: MutationNames, returParams?: any): any => {
  const mutation = {
    login: print(
      gql`
        mutation($email: String!, $password: String!) {
          login(email: $email, password: $password) {
            token
            refreshToken
          }
        }
      `
    ),
    register: print(
      gql`
        mutation(
          $firstName: String!
          $email: String!
          $lastName: String!
          $age: DateTime!
          $jobTitle: String!
          $file: Upload
        ) {
          register(
            input: {
              FirstName: $firstName
              LastName: $lastName
              Email: $email
              Age: $age
              JobTitle: $jobTitle
              File: $file
            }
          )
        }
      `
    ),

  }[mutate];

  if (!mutation) return {};

  return mutation;
};

在这种情况下,我使用了寄存器突变。

我有一些关于如何处理数据获取的钩子,所以我不打算包含它,因为它有很多代码。数据在前端正确获取,在发布到后端之前,我将所有内容都放入一个 formData 对象:

  const submitForm: SubmitForm = (obj: SendObject) => {
    const Fdata = new FormData();

    Fdata.append('0', fileImp.file);

    Fdata.append('operations', JSON.stringify(obj.data));

    const map = {
      '0': ['variables.file'],
    };
    Fdata.append('map', JSON.stringify(map));

    callAxiosFn(
      {
        method,
        url: 'http://localhost:4000/api',
        data: Fdata,
        // headers: obj.headers,
      },
      qlType.toString()
    );
  };

这样调用:

  const response = await axios({
    headers: {
      Accept: 'application/json',
      'x-token': localStorage.getItem('token'),
      'x-refresh-token': localStorage.getItem('refreshToken'),
      ...(config.headers || {}),
    },
    ...config,
  });

配置为AxiosRequestConfig

我发送的内容:

我不完全理解 formdata 将如何到达我的解析器端点,因此我在后端返回后做错了什么:

(node:748) UnhandledPromiseRejectionWarning: [object Array] (node:748) UnhandledPromiseRejectionWarning:未处理的承诺拒绝。这 错误源于在异步函数内部抛出 没有 catch 块,或拒绝未处理的承诺 使用 .catch()。 (拒绝 ID:1)(节点:748)[DEP0018] DeprecationWarning:不推荐使用未处理的承诺拒绝。在 未来,未处理的承诺拒绝将终止 具有非零退出代码的 Node.js 进程。

我意识到这很多,但我在这里的 vits 结束时,一整天都在这。任何帮助都深表感谢。

编辑:

由于我的后端受到质疑,我想我只是表明,在发送数据而不像上面那样附加 Formdata 的情况下,我可以让它工作:

  const submitForm: SubmitForm = (obj: SendObject) => {

    callAxiosFn(
      {
        method,
        url: 'http://localhost:4000/api',
        data: obj.data,
      },
      qlType.toString()
    );
  };

obj.data 是:

{query: "mutation ($firstName: String!, $email: String!, $l… Age: $age, JobTitle: $jobTitle, File: $file})↵}↵", variables: {…}}
query: "mutation ($firstName: String!, $email: String!, $lastName: String!, $age: DateTime!, $jobTitle: String!, $file: Upload) {↵  register(input: {FirstName: $firstName, LastName: $lastName, Email: $email, Age: $age, JobTitle: $jobTitle, File: $file})↵}↵"
variables:
age: "1977-04-04"
email: "JhoneDoe@hotmail.com"
file: File {name: "something.jpg", lastModified: 1589557760497, lastModifiedDate: Fri May 15 2020 17:49:20 GMT+0200 (centraleuropeisk sommartid), webkitRelativePath: "", size: 32355, …}
firstName: "Jhon"
jobTitle: "SomethingCool"
lastName: "Doe"
password: "CoolPassword!"123"
__proto__: Object
__proto__: Object

在浏览器中发送查询:

后端接收数据但不包括图像:

编辑:

最近发现我的 fastify 后端可能有读取 formData 的问题。 尝试安装

fastify-multipart

但注册时出错:

FST_ERR_CTP_ALREADY_PRESENT(contentType) ^ FastifyError [FST_ERR_CTP_ALREADY_PRESENT]:

之后我尝试了:

npm uninstall fastify-file-upload

错误仍然存​​在。

【问题讨论】:

  • 错误的变异语法 - 没有返回内容 def - 你必须返回一些东西 - 至少有一些 { id }
  • file 不是null-ed
  • Fdata.append('file'... - 为什么不是“0”? ....恕我直言,您应该将问题分开...获取有效的客户端配方(第一个链接)并使后端正常工作...然后使用所需的自定义重新创建它...一次更改太多
  • 更新了问题以在我不将数据作为 formData 发送时显示功能齐全的后端。那么问题是我没有点击解析器并且文件也不包括在内。
  • mutation name( input ) { result } - 您的查询中有 no result part(我不关心代码).....从 workimg formData (FE ) 示例 并证明 BE 正在工作......然后根据您的需求调整 FE

标签: node.js graphql axios apollo-server fastify


【解决方案1】:

@CodingLittle 很高兴您发现 the answer 与多部分表单字段排序有关。

一些要补充的东西(回答是因为我没有对您的回答发表评论所需的 50 声望,尽管我是 graphql-upload 作者)...

另外请注意,在我的问题中,我错过了在变量中将文件本身设置为 null

这是真的,而且做得对,虽然实际上很多GraphQL multipart request spec 服务器实现只会简单地用上传标量值替换文件映射路径中的任何内容,而不关心那里有什么——理论上,你可以用asdf而不是null替换变量中的文件,它仍然可以工作。 JSON.stringify 会用 {} 之类的东西替换文件实例。

如果后端以明确的 400 状态和描述性错误消息响应而不是抛出粗糙的 UnhandledPromiseRejectionWarning 错误,则可以避免很多麻烦。如果您的graphql-upload 依赖项在后端是最新的,那么当请求不符合GraphQL multipart request spec 关于field ordering 时,您应该看到描述性错误消息,如graphql-upload 测试中所示:

https://github.com/jaydenseric/graphql-upload/blob/v11.0.0/test/public/processRequest.test.js#L929

尝试在您的后端项目中运行 npm ls graphql-upload 以检查仅安装了一个版本,并且它是最新发布到 npm 的版本(本回答时为 v11)。请注意,如果您依赖 Apollo Server 为您安装它,他们使用的是非常过时的版本 (v8)。

【讨论】:

    【解决方案2】:

    这需要一些时间,通常当您认为某些事情是理所当然的时,需要时间来发现错误。

    对于遇到相同问题的任何人,请记住您添加内容的顺序很重要!

    我做了什么:

    const Fdata = new FormData();
    
    Fdata.append('0', fileImp.file);  // NOTICE THIS
    
    Fdata.append('operations', JSON.stringify(obj.data));
    
    const map = { // NOTICE THIS
      '0': ['variables.file'],
    };
    Fdata.append('map', JSON.stringify(map));
    

    问题: 还记得我说过添加事物的顺序很重要吗?那么这里的情况是在添加文件之后添加了映射。

    正确方法:

    const Fdata = new FormData();
    
    Fdata.append('operations', JSON.stringify(obj.data));
    
    const map = { // NOTICE THIS
      '0': ['variables.file'],
    };
    Fdata.append('map', JSON.stringify(map));
    Fdata.append('0', fileImp.file);  // NOTICE THIS
    

    还请注意,在我的问题中,我错过了在变量中将文件本身设置为 null:

      variables: {
        file: null,
      },
    

    这是必须要做的。

    欲了解更多信息read here

    【讨论】:

    • 恭喜,您可以提前几天完成这项工作......只需更严格地遵循您遵循的指南:D
    【解决方案3】:

    好吧,我还没有探讨过这个话题。但我知道带有 GraphQLaxios 并不能很好地工作。 Axios 主要用于 REST API 调用。但是,我真的很喜欢这个频道Ben Awad,并从中学到了很多东西。这家伙真的很棒,并且清楚而漂亮地解释了事情。但最重要的是,他是一个 GraphQL 爱好者,并探索和展示了关于它的各种主题,以及 React.jsTypeORMPostgreSQL。以下是他频道中的一些有用链接,可能有助于解决您的问题:

    我希望这会有所帮助!如果内容有帮助,请告诉我!

    【讨论】:

    • 非常有用的链接,我也关注 Ben 的视频!我已经使用 axios 实现了所有功能,并且在尝试发送文件数据时,它现在可以完美地处理异常。
    • 听到这个消息我真的很高兴!总是乐于助人。请,如果您也能接受我的回答,我将不胜感激。
    • 我很欣赏这些链接并认为它很有用,但这不是我问题的答案。我拒绝接受这不能完成,我在某个地方搞砸了。
    • 好的,我也很感谢您的诚实回答。至少我希望我对您的问题有所帮助。
    猜你喜欢
    • 2020-03-21
    • 2017-06-29
    • 2017-09-22
    • 2019-02-14
    • 1970-01-01
    • 2021-11-30
    • 2020-06-24
    • 2020-08-17
    • 2020-09-04
    相关资源
    最近更新 更多