将“本地”文件从 nodejs 上传到 rails graphql 后端(服务器到服务器)

Posted

技术标签:

【中文标题】将“本地”文件从 nodejs 上传到 rails graphql 后端(服务器到服务器)【英文标题】:Uploading a "LOCAL" file from nodejs to rails graphql backend (server to server) 【发布时间】:2021-12-06 04:27:37 【问题描述】:

这与“客户端到服务器”上传无关。

我正在寻找一种将文件从 nodejs 服务器上传到 rails 后端 graphql 服务器(服务器到服务器)的方法。 rails 后端工作正常,如果来自客户端,它可以毫无问题地上传文件。

我遇到的问题是在 nodejs 中创建/构建一个"File" object,这样我就可以使用这样的 Apollo 突变来上传文件。

// nodejs server side, NOT react client side
const  data  = await client.mutate<
FileUploadMutation,
FileUploadMutationVariables
>(
  mutation: FileUploadDocument,
  variables: 
    input: 
      file: file // how do I create this file object from a local file?
    ,
  
)

如果是客户端,那就很简单了。

<input type="file" onchange=(event) => 
  const file = event.target.files[0]  // this can be the "file" object
/>

是否可以在nodejs中创建一个与the client side File object等效的文件对象?

import fs from "fs"

// giving this "file" object to the mutation doen't upload the file
const file = fs.createReadStream("./local/file/path.jpg")

我尝试过的事情

使用apollo_upload_server 创建了一个新的rails graphql 后端 创建了一个新的 nextjs posting a file working 在服务器端 (Altair GraphQL Client) 我看到的文件是&lt;ActionDispatch::Http::UploadedFile:0x00007f9e1ac0e8a0 @tempfile=#&lt;Tempfile:/tmp/RackMultipart20211020-14-156qwls.jpg ... 创建了一个 Blob 对象,但我在前端收到此错误Variable $input of type TestMutationInput! was provided invalid value for file ( is not a valid upload)

package.json


  "scripts": 
    "dev": "next",
    "generate": "graphql-codegen -w --config codegen.yml"
  ,
  "dependencies": 
    "@apollo/client": "^3.4.16",
    "apollo-upload-client": "^16.0.0",
    "graphql": "^15.6.1",
    "next": "^11.1.2",
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  ,
  "devDependencies": 
    "@graphql-codegen/cli": "^2.2.1",
    "@graphql-codegen/typescript": "^2.2.4",
    "@graphql-codegen/typescript-operations": "^2.1.8",
    "@graphql-codegen/typescript-react-apollo": "^3.1.6",
    "@types/apollo-upload-client": "^14.1.0",
    "@types/react": "^17.0.30",
    "typescript": "^4.4.4"
  

(nextjs)/api/test.ts (nodejs)

import  ApolloClient, InMemoryCache  from "@apollo/client"
import  createUploadLink  from "apollo-upload-client"
import  Blob  from "buffer"
import type  NextApiRequest, NextApiResponse  from "next"
// import  Blob  from "node:buffer"
import * as Generated from "src/generated/graphql"

const client = new ApolloClient(
  cache: new InMemoryCache(),
  link: createUploadLink(
    uri: "http://backend:3000/graphql",
  ),
)

// checking if the apollo connection is good at all ------------
client
  .query<Generated.TestFieldQuery, Generated.TestFieldQueryVariables>(
    query: Generated.TestFieldDocument,
    variables: ,
  )
  .then(( data ) => 
    // this actually returns data
    console.log("data:", data)
  )
// checking if the apollo connection is good at all ------------


export default async (req: NextApiRequest, res: NextApiResponse) => 
  // const buffer = fs.readFileSync(fs.realpathSync("./test.jpg"))
  // const file = new Blob([buffer],  type: "image/jpg" )
  const file = new Blob(["abc123"],  type: "text/plain" )
  console.log("file:", file)

// this mutation gives me this error
//   graphQLErrors: [
//    
//      message: 'Variable $input of type TestMutationInput! was provided invalid value for file ( is not a valid upload)',
//      locations: [Array],
//      extensions: [Object]
//    
//  ],
  await client.mutate<
    Generated.TestMutationMutation,
    Generated.TestMutationMutationVariables
  >(
    mutation: Generated.TestMutationDocument,
    variables: 
      input: 
        file,
      ,
    ,
    fetchPolicy: "no-cache",
  )

  res.status(200).end()

【问题讨论】:

GraphQL 不支持文件上传,甚至 Apollo 也没有提供开箱即用的 this。 Rails 后端期望文件采用什么格式?如果你在浏览器中,你会如何处理file "这与“客户端到服务器”上传无关。" - 当然,就像所有 HTTP 一样,只是您有一个 node.js 客户端而不是浏览器客户。这就是您仍在使用 Apollo client 库的原因。 事实上,我在客户端使用的是 jaydenseric/apollo-upload-client v12.1.0。所以就像我说的,rails 后端可以正常接收文件上传。 "client" 我的意思更像是“浏览器”。没有冒犯,但是,是的,我使用的是 Apollo 客户端,所以如果你从字面上理解它是“客户端到服务器”。但我更喜欢关注如何在nodejs中创建一个像客户端(浏览器)一样的“文件对象”。 啊,所以 rails 服务器希望根据 Apollo 规范进行分段上传?你可以手工制作。另外,你见过github.com/jaydenseric/apollo-upload-client/issues/134、github.com/jaydenseric/apollo-upload-client/issues/32和github.com/jaydenseric/apollo-upload-client/issues/129吗? 【参考方案1】:

我成功了。

https://github.com/jaydenseric/apollo-upload-client/issues/272

import  ApolloClient, InMemoryCache  from "@apollo/client"
import  createUploadLink, isExtractableFile  from "apollo-upload-client"
import FormData from "form-data"
import fs,  ReadStream  from "fs"
import type  NextApiRequest, NextApiResponse  from "next"
import * as Generated from "src/generated/graphql"

const client = new ApolloClient(
  cache: new InMemoryCache(),
  link: createUploadLink(
    uri: "https://backend/graphql",
    // https://nextjs.org/docs/basic-features/supported-browsers-features#server-side-polyfills
    // nextjs already polyfills this
    // fetch: ...
    // yarn add form-data or in my case @graphql-codegen had it
    FormData,  
    isExtractableFile: (value) =>
      isExtractableFile(value) ||
      (typeof ReadStream !== "undefined" && value instanceof ReadStream),
  ),
)

export default async (req: NextApiRequest, res: NextApiResponse) => 
  const file = fs.createReadStream(fs.realpathSync("./test.jpg"))

  await client.mutate<
    Generated.TestMutationMutation,
    Generated.TestMutationMutationVariables
  >(
    mutation: Generated.TestMutationDocument,
    variables: 
      input: 
        file,
      ,
    ,
    fetchPolicy: "no-cache",
  )

  res.status(200).end()

【讨论】:

以上是关于将“本地”文件从 nodejs 上传到 rails graphql 后端(服务器到服务器)的主要内容,如果未能解决你的问题,请参考以下文章

Objective C - NodeJS - 如何将特定文件(不是图像)从 iOS 应用程序上传到服务器?

node批量读取本地图片并上传

将文本文件从 Android 上传到 Rails 会导致内容类型欺骗错误

将Ruby on Rails项目从github上传到AWS EC2实例错误

nodejs实现本地上传图片并预览功能

如何使用 NodeJS 将文件或图像上传到服务器