在前端开发过程中,文件上传是一个常见的需求。而 GraphQL 是一种用于 API 的查询语言和运行时环境,使用它可以更方便地管理 API,并且支持对多个资源的复杂查询。本文将介绍如何使用 GraphQL 进行文件上传,并提供基于 Node.js 的示例代码。
GraphQL 对文件上传的支持
在 GraphQL 中,文件上传并不是一个内置的功能。GraphQL 建议使用外部库或插件来处理文件上传。常见的解决方案是使用 apollo-upload-server 或 graphql-upload 等库或插件。这些库或插件都提供了方便的解决方案,使得在 GraphQL 中处理文件上传变得容易。
如何使用 apollo-upload-server
在本文中,我们将介绍如何使用 apollo-upload-server 进行文件上传。apollo-upload-server 是一个 GraphQL 上传插件,它通过 GraphQL multipart request specification 提供了文件上传功能。
首先,我们需要依赖两个库:apollo-server-express
和 apollo-upload-server
。你可以使用 NPM 进行安装:
npm install apollo-server-express apollo-upload-server
然后,我们需要在 Express 应用中启用文件上传功能。以下是一个简单的示例,它使用 graphqlUploadExpress
中间件启用上传处理:
// javascriptcn.com 代码示例 const express = require('express'); const { ApolloServer } = require('apollo-server-express'); const { graphqlUploadExpress } = require('apollo-upload-server'); const typeDefs = require('./schema'); const resolvers = require('./resolvers'); const app = express(); const server = new ApolloServer({ typeDefs, resolvers, uploads: false, context: async ({ req }) => { return { user: req.user }; }, }); app.use(graphqlUploadExpress()); server.applyMiddleware({ app }); app.listen({ port: 4000 }, () => { console.log(`Apollo Server running at http://localhost:4000${server.graphqlPath}`); });
在这个示例中,graphqlUploadExpress
中间件用于启用上传处理。请注意,我们将上传选项设置为 false,这意味着文件上传将启用。你可以通过将 uploads 设为 true 来禁用文件上传功能。
下一步是在 GraphQL Schema 中定义上传类型。以下是一个包含上传类型的 Schema 示例:
// javascriptcn.com 代码示例 type File { filename: String! mimetype: String! encoding: String! url: String! } type Query { hello: String } type Mutation { uploadFile(file: Upload!): File! }
在这个示例中,我们定义了一个 File 类型,它包含文件名、MIME 类型、编码类型和文件的 URL。我们还定义了一个 Mutation 类型,它包含一个上传文件的方法 uploadFile
。为了支持文件上传,我们定义了 file
参数的类型为 Upload
,即 GraphQL multipart request specification 中提供的上传类型。
最后,我们需要在 Mutation Resolver 中处理文件上传并将文件信息返回给客户端。以下是一个解决方案:
// javascriptcn.com 代码示例 const { createWriteStream } = require('fs'); const path = require('path'); const resolvers = { Query: { hello: () => 'world', }, Mutation: { async uploadFile(parent, { file }) { const { createReadStream, filename, mimetype, encoding } = await file; const stream = createReadStream(); const filePath = path.join(__dirname, `./uploads/${filename}`); const url = `http://localhost:4000/uploads/${filename}`; await new Promise((resolve, reject) => { stream .on('error', (error) => { if (stream.truncated) { // Delete the truncated file fs.unlinkSync(filePath); } reject(error); }) .pipe(createWriteStream(filePath)) .on('error', (error) => reject(error)) .on('finish', () => resolve()); }); return { filename, mimetype, encoding, url }; }, }, }; module.exports = resolvers;
在这个示例中,我们首先获取上传文件的流和文件信息,然后将流写入到磁盘上,最后将文件信息返回给客户端。
文件上传的 Web 界面
完成了上述的配置和代码,我们会发现文件上传已经支持,但我们还需要为用户提供界面进行交互上传操作,以下是基于 React 的示例代码:
// javascriptcn.com 代码示例 import React from 'react'; import { gql, useMutation } from '@apollo/client'; import { Upload, message } from 'antd'; import { UploadOutlined } from '@ant-design/icons'; const UPLOAD_FILE = gql` mutation UploadFile($file: Upload!) { uploadFile(file: $file) { filename mimetype encoding url } } `; function App() { const [uploadFile] = useMutation(UPLOAD_FILE, { onCompleted: (data) => message.success('文件上传成功'), onError: (error) => message.error(error.message), }); return ( <div style={{ margin: '20px auto', maxWidth: '500px' }}> <Upload accept="image/*" action={async (file) => { const { filename, type, size } = file; const response = await uploadFile({ variables: { file, }, }); console.log(response); return { filename, type, size, url: response.data.uploadFile.url, }; }} beforeUpload={() => false} > <div style={{ padding: '20px', border: '1px solid #ccc' }}> <UploadOutlined /> <div>单击或将文件拖放到此处上传</div> </div> </Upload> </div> ); }
在这个示例中,我们使用 Upload
组件提供文件上传的 Web 界面,并使用 useMutation
钩子发送上传请求。在上传成功时,我们会给用户反馈。此外,我们还使用了 Ant Design 的 UploadOutlined
图标。
总结
本文介绍了如何使用 apollo-upload-server 和 GraphQL 完成文件上传,并提供了基于 Node.js 的示例代码。处理文件上传是一项常见的任务,这里所介绍的解决方案可以帮助我们简化开发工作,提高效率。同时,GraphQL 的查询语言和运行时环境也让我们可以更方便地管理 API 和复杂的查询。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6530db4c7d4982a6eb26c2d4