如何使用 axios 将图片上传到 fastify + graphql 后端?
How to upload images to fastify + graphql backend with axios?
当通过 axios 发送图像时,我发现我必须使用 formdata。我在这里添加我的图片,但是当发送表单数据时,我的整个后端都冻结了,只是说 "pending".
我一直在关注 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
我发送的内容:
我不太明白表单数据如何到达我的解析器端点,因此我在后端做错了事 returns:
(node:748) UnhandledPromiseRejectionWarning: [object Array] (node:748)
UnhandledPromiseRejectionWarning: Unhandled promise rejection. This
error originated either by throwing inside of an async function
without a catch block, or by rejecting a promise which was not handled
with .catch(). (rejection id: 1) (node:748) [DEP0018]
DeprecationWarning: Unhandled promise rejections are deprecated. In
the future, promise rejections that are not handled will terminate the
Node.js process with a non-zero exit code.
我知道这很多,但我在这里的生命即将结束,一整天都在做这件事。非常感谢任何帮助。
编辑:
由于我的后端受到质疑,我想我会像上面那样在不附加 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
错误依然存在。
好吧,我还没有探讨这个话题。但我知道 axios 和 GraphQL 并不能很好地工作。 Axios 主要用于 REST API 调用。不过,我真的很喜欢这个频道,也从中学到了很多东西 Ben Awad。这家伙真的很棒,解释清楚,很好。但最重要的是,他是一名 GraphQL 爱好者,探索并提出了有关它的各种主题,以及 React.js、TypeORM & PostgreSQL。以下是他频道中的一些有用链接,可能有助于解决您的问题:
希望对您有所帮助!如果内容有帮助,请告诉我!
这需要一些时间,通常当您认为某些事情是理所当然的时候,发现错误需要时间。
对于遇到相同问题的任何人,请记住添加内容的顺序很重要!
我做了什么:
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
另请注意,在我的问题中,我错过了在变量中将文件本身设置为空:
variables: {
file: null,
},
这是必须要做的。
了解更多信息read here
@CodingLittle 很高兴您发现 与多部分表单字段排序有关。
一些要补充的东西(尽管我是 graphql-upload
的作者,但我没有对您的回答发表评论所需的 50 声望)…
Also note that in my qestion I missed setting the file itself to null in the variables
这是真的,而且很正确,尽管实际上很多 GraphQL multipart request spec 服务器实现将简单地用上传标量值替换文件映射路径中的任何内容,而不关心那里有什么— 理论上,您可以将变量中的文件替换为 asdf
而不是 null
,它仍然有效。 JSON.stringify
会用 {}
.
之类的内容替换文件实例
如果后端以清晰的 400 状态和描述性错误消息进行响应,而不是抛出一个粗糙的 UnhandledPromiseRejectionWarning
错误,那么您的许多头痛问题就可以避免了。如果您的 graphql-upload
依赖项在后端是最新的,当请求不符合关于 字段排序 的 GraphQL multipart request spec 时,您应该会看到一条描述性错误消息,正如在 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)。
当通过 axios 发送图像时,我发现我必须使用 formdata。我在这里添加我的图片,但是当发送表单数据时,我的整个后端都冻结了,只是说 "pending".
我一直在关注 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
我发送的内容:
我不太明白表单数据如何到达我的解析器端点,因此我在后端做错了事 returns:
(node:748) UnhandledPromiseRejectionWarning: [object Array] (node:748) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:748) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
我知道这很多,但我在这里的生命即将结束,一整天都在做这件事。非常感谢任何帮助。
编辑:
由于我的后端受到质疑,我想我会像上面那样在不附加 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
错误依然存在。
好吧,我还没有探讨这个话题。但我知道 axios 和 GraphQL 并不能很好地工作。 Axios 主要用于 REST API 调用。不过,我真的很喜欢这个频道,也从中学到了很多东西 Ben Awad。这家伙真的很棒,解释清楚,很好。但最重要的是,他是一名 GraphQL 爱好者,探索并提出了有关它的各种主题,以及 React.js、TypeORM & PostgreSQL。以下是他频道中的一些有用链接,可能有助于解决您的问题:
希望对您有所帮助!如果内容有帮助,请告诉我!
这需要一些时间,通常当您认为某些事情是理所当然的时候,发现错误需要时间。
对于遇到相同问题的任何人,请记住添加内容的顺序很重要!
我做了什么:
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
另请注意,在我的问题中,我错过了在变量中将文件本身设置为空:
variables: {
file: null,
},
这是必须要做的。
了解更多信息read here
@CodingLittle 很高兴您发现
一些要补充的东西(尽管我是 graphql-upload
的作者,但我没有对您的回答发表评论所需的 50 声望)…
Also note that in my qestion I missed setting the file itself to null in the variables
这是真的,而且很正确,尽管实际上很多 GraphQL multipart request spec 服务器实现将简单地用上传标量值替换文件映射路径中的任何内容,而不关心那里有什么— 理论上,您可以将变量中的文件替换为 asdf
而不是 null
,它仍然有效。 JSON.stringify
会用 {}
.
如果后端以清晰的 400 状态和描述性错误消息进行响应,而不是抛出一个粗糙的 UnhandledPromiseRejectionWarning
错误,那么您的许多头痛问题就可以避免了。如果您的 graphql-upload
依赖项在后端是最新的,当请求不符合关于 字段排序 的 GraphQL multipart request spec 时,您应该会看到一条描述性错误消息,正如在 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)。