GraphQL 是一种新型的 API 查询语言,它可以为 Web 应用程序提供更加精细、专注的数据查询能力。在 GraphQL 中,我们可以使用类似于 RESTful API 的方式获取或修改数据,但是 GraphQL 更加灵活、可扩展、类型安全和高效。
在实际的 Web 开发中,我们通常需要上传一些文件,例如图片、视频、音频等。而 GraphQL 的标准规范并没有提供文件上传的官方方法。本文将介绍一种在 GraphQL 中实现文件上传的方法,并提供一个基于 Node.js 和 Apollo Server 的示例代码。
实现方法
在 GraphQL 中实现文件上传的方法有很多种,例如使用 base64 编码、使用 FormData、使用 Multipart 等。这里我们选择使用 Multipart。
Multipart 是一种常见的 HTTP 请求体格式,通常用于上传文件、表单等数据。它的基本格式如下:
// javascriptcn.com 代码示例 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryxxxxxxxxxx ------WebKitFormBoundaryxxxxxxxxxx Content-Disposition: form-data; name="field1" value1 ------WebKitFormBoundaryxxxxxxxxxx Content-Disposition: form-data; name="field2" value2 ------WebKitFormBoundaryxxxxxxxxxx Content-Disposition: form-data; name="file"; filename="example.txt" Content-Type: text/plain (file content) ------WebKitFormBoundaryxxxxxxxxxx--
其中,boundary 是一个随机生成的字符串,用于标示内容的边界;每个字段都用 Content-Disposition
首部指定名称,文件名和字段类型;文件的内容则放在 Content-Type
首部指定的媒体类型的数据块中。
在 GraphQL 中,我们可以定义一个 Upload
标量类型,用于表示上传的文件。对于文件的传输,我们可以在 GraphQL Schema 中定义一个 Mutation 类型,并提供一个参数类型为 Upload
的字段,用于接收前端传递的 Multipart 请求。在后端实现中,我们可以使用一些现有的 Node.js 模块,例如 multer
、formidable
、busboy
等,来解析 Multipart 请求并将文件存储到服务器上。
下面是一个基于 Node.js 和 Apollo Server 的示例代码,用于实现 GraphQL 中的文件上传功能:
// javascriptcn.com 代码示例 const { ApolloServer, gql } = require('apollo-server'); const fs = require('fs'); const { createWriteStream } = require('fs'); const { join } = require('path'); const { v4 } = require('uuid'); const formidable = require('formidable'); const typeDefs = gql` scalar Upload type Mutation { uploadFile(file: Upload!): String } `; const resolvers = { Mutation: { uploadFile: async (parent, { file }) => { const { createReadStream, filename, mimetype } = await file; const id = v4(); const uploadDir = join(__dirname, 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); } const filePath = join(uploadDir, `${id}-${filename}`); await new Promise((resolve, reject) => createReadStream() .pipe(createWriteStream(filePath)) .on('finish', () => resolve()) .on('error', () => reject()), ); return filePath; }, }, }; const server = new ApolloServer({ typeDefs, resolvers, context: ({ req }) => { const context = {}; const contentType = req.headers['content-type'] || ''; const isFormData = contentType.startsWith('multipart/form-data'); if (isFormData) { const form = formidable({ multiples: true }); const files = {}; const fields = {}; context.filePromise = new Promise((resolve, reject) => { form.on('fileBegin', (name, file) => { files[name] = file; }); form.on('field', (name, value) => { fields[name] = value; }); form.on('file', (name, file) => { files[name] = file; }); form.on('end', () => { context.variables = { ...context.variables, ...fields, ...files, }; resolve(); }); form.parse(req); }); } return context; }, }); server.listen(4000).then(({ url }) => { console.log(`Server ready at ${url}`); });
本例中使用的是 formidable
模块来解析 Multipart 请求,并使用 createReadStream
和 createWriteStream
方法来读取和写入文件内容。上传的文件会被保存到项目根目录下的 uploads
文件夹中,并以 uuid 和文件名的组合命名。
使用方法
在前端使用 GraphQL 来上传文件时,我们需要将文件内容转换成 Multipart 格式,并将请求发送到后端的 GraphQL API。下面是使用 fetch
函数的示例代码:
// javascriptcn.com 代码示例 const uploadFile = async (file) => { const form = new FormData(); form.append('operations', JSON.stringify({ query: ` mutation ($file: Upload!) { uploadFile(file: $file) } `, variables: { file: null, }, })); form.append('map', JSON.stringify({ file: ['variables.file'], })); form.append('file', file); const response = await fetch('http://localhost:4000', { method: 'POST', body: form, }); const { data, errors } = await response.json(); if (errors) { throw new Error(errors[0].message); } return data.uploadFile; }; const fileInput = document.getElementById('fileInput'); fileInput.addEventListener('change', async (event) => { const file = event.target.files[0]; if (!file) { return; } try { const filePath = await uploadFile(file); console.log(`File uploaded to: ${filePath}`); } catch (error) { console.log(`File upload failed: ${error.message}`); } });
在上面的代码中,首先创建了一个包含 GraphQL 查询和变量的对象 operations
,并通过 map
对象指定哪些变量对应哪些文件。然后将文件内容添加到 FormData 中,并使用 fetch
发送一个 POST 请求到 GraphQL API。最后解析响应,获取上传成功后的文件路径。
总结
本文介绍了如何在 GraphQL 中实现文件上传的方法,并提供了一个基于 Node.js 和 Apollo Server 的示例代码。通过本文的学习,我们可以掌握在 GraphQL 中处理 Multipart 请求和文件上传的技能,在实践中更好地应用 GraphQL 技术,为 Web 应用带来更加灵活、高效、安全的数据管理能力。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652d088b7d4982a6ebe859bd