将“本地”文件从 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) 我看到的文件是<ActionDispatch::Http::UploadedFile:0x00007f9e1ac0e8a0 @tempfile=#<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 应用程序上传到服务器?
将文本文件从 Android 上传到 Rails 会导致内容类型欺骗错误