GraphQL 是一种新兴的 API 技术,它可以更好地满足现代应用程序的需求。然而,GraphQL 在处理文件上传方面存在一些挑战。在本文中,我们将探讨如何解决 GraphQL 中的文件上传问题。
问题描述
在 GraphQL 中,我们可以使用 mutation 定义一个上传文件的操作。例如:
mutation {
uploadFile(file: File!) {
url
}
}其中,File 是一个自定义的 scalar 类型,它表示一个文件对象。我们需要将文件对象作为参数传递给 uploadFile 操作,并返回一个 URL,表示上传后的文件在服务器上的位置。
但是,如何将文件对象传递给 GraphQL 服务器呢?在传统的 HTTP API 中,我们可以使用 multipart/form-data 格式来上传文件。但是,在 GraphQL 中,我们通常使用 JSON 格式来传递数据,因此,我们需要找到一种适合 GraphQL 的文件上传方式。
解决方案
方案一:使用 Base64 编码
一种解决方案是将文件转换为 Base64 编码,并将编码后的字符串作为参数传递给 GraphQL 服务器。例如:
mutation {
uploadFile(data: String!) {
url
}
}其中,data 是一个字符串,表示经过 Base64 编码后的文件内容。在服务器端,我们可以将字符串解码为二进制数据,并将其保存到文件系统中。
这种方案的优点是,它可以与现有的 GraphQL 实现兼容,因为它只是一个普通的字符串参数。但是,它的缺点是,它会增加数据传输的大小,因为 Base64 编码会使文件大小增加约 33%。
方案二:使用 multipart/form-data
另一种解决方案是使用 multipart/form-data 格式来上传文件。在这种方案中,我们需要使用一个特殊的 GraphQL 客户端,它可以将文件转换为 multipart/form-data 格式,并将其发送到服务器。例如,使用 apollo-upload-client:
import { createUploadLink } from 'apollo-upload-client';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
const client = new ApolloClient({
link: createUploadLink({ uri: '/graphql' }),
cache: new InMemoryCache(),
});在服务器端,我们需要使用一个特殊的 GraphQL 中间件来处理 multipart/form-data 格式的请求。例如,使用 graphql-upload:
// www.javascriptcn.com code example
import { ApolloServer } from 'apollo-server-express';
import { graphqlUploadExpress } from 'graphql-upload';
const server = new ApolloServer({
typeDefs,
resolvers,
uploads: false,
});
app.use(graphqlUploadExpress());
server.applyMiddleware({ app });这种方案的优点是,它可以减少数据传输的大小,并且可以与现有的文件上传库兼容。但是,它需要使用特殊的客户端和服务器端实现。
示例代码
下面是一个使用 apollo-upload-client 和 graphql-upload 实现文件上传的示例代码:
客户端代码
// www.javascriptcn.com code example
import React from 'react';
import { useMutation } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
import { createUploadLink } from 'apollo-upload-client';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
const UPLOAD_FILE = gql`
mutation($file: Upload!) {
uploadFile(file: $file) {
url
}
}
`;
const client = new ApolloClient({
link: createUploadLink({ uri: '/graphql' }),
cache: new InMemoryCache(),
});
function App() {
const [uploadFile] = useMutation(UPLOAD_FILE);
const handleUpload = async (event) => {
const file = event.target.files[0];
const result = await uploadFile({ variables: { file } });
console.log(result.data.uploadFile.url);
};
return (
<div>
<input type="file" onChange={handleUpload} />
</div>
);
}
export default App;服务器端代码
// www.javascriptcn.com code example
import { ApolloServer } from 'apollo-server-express';
import { graphqlUploadExpress } from 'graphql-upload';
const typeDefs = `
scalar Upload
type File {
url: String!
}
type Mutation {
uploadFile(file: Upload!): File!
}
`;
const resolvers = {
Mutation: {
uploadFile: async (parent, { file }) => {
const { createReadStream, mimetype } = await file;
const stream = createReadStream();
const url = await uploadToS3(stream, mimetype);
return { url };
},
},
};
const server = new ApolloServer({
typeDefs,
resolvers,
uploads: false,
});
app.use(graphqlUploadExpress());
server.applyMiddleware({ app });其中,uploadToS3 是一个将文件上传到 Amazon S3 的函数。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d8e267a941bf7134fe7f2d